Клуб программистов "Весельчак У" - Советы по Windows (7.3)
ГЛАВНАЯ СТАТЬИ ЧАВО КОНТАКТЫ ПОИСК
новости

Наш сайт поздравляет всех с Новым Годом! Желаем вам всего самого лучшего и много много хорошего в Новом 2010 году.
Администрация.


В связи с дырой в старом движке форума все переезжает на новый движок. Прошу прощения за неудобcтва...


Свершилось!
Сайт переехал на свой собственный выделенный сервер!

личный кабинет
онлайн
счетчики



Яндекс цитирования



Советы по Windows (7.3)
4. Алгоритм. Вернемся в начало и вспомним, какие типы действий у нас были. Начнем работу с отработки действий, которые не зависят от пользователя и будут отрабатываться самостоятельно. Возьмем наш объект ATetris и опишем его задачи. а) старт игры. б) выкладывание случайного нового элемента в стакан. в) сдвиг текущего элемента вниз с проверкой на уже окончательное достижение позиции в стакане. г) проверка на наличие заполненных линий в стакане и их стирание с последующим перемещением всего содержимого стакана. Опишем старт игры. Для нее нам понядобятся следующие данные, дабы оптимизировать процесс старта.... Добавим: Код: private: bool IsStart; // флаг стартовала игра уже или нет. int start_x,start_y; // стартовые значения для левого верхнего угла нового блока. int current_position; // позиция текущего блока (степень его повернутости). Если вы помните, у нас есть все 4 позиции каждого блока, изначально она нулевая и меняется в зависимости от количества нужных поворотов. Код: int current_block; // собственно, номер текущего падающего блока в массиве ABlock объектов. int LinesOut[MAXY]; // Массив для обнаруженных полных линий. Его необходимость станет понятна чуть позже. int Error_subject; // виды ошибок при выходе из рабочих процедур. Похоже на LastError в Windows unsigned int Points; // Очки, набранные игроком unsigned int Timeout; // текущий таймаут между шагами игры. unsigned int Level; // текущий уровень - изначально 0. Добавим теперь две, первые после инициализации, функции Код: public: void CreateBaseTetris(CDC * pDC); void DestroyBaseTetris(); Первая, естественно, будет работать для создания базового поля, вторая - для его уничтожения... Вот код... Код: void ATetris::CreateBaseTetris(CDC * pDC) { m_Place.CreateBasePlace(pDC); } void ATetris::DestroyBaseTetris() { m_Place.DestroyBasePlace(); } Собственно, все. Ведь мы уже позаботились о том, что мы инициализируем и как, еще при работе с APlace. Теперь нам в алгоритме надо позаботиться о вызове методов, не более... Очень нужная нам функция... Код: public: CDC * GetTetrisDC(void); CDC * ATetris::GetTetrisDC() { return m_Place.GetBaseDC(); } Опять, как видите, запрос к APlace. Таким образом, работа объекта ATetris - служить фильтром между данными и программным интерфейсом, обеспечивая работу алгоритма. Что, собственно, и требовалось доказать. Если вы пойдете глубже, то поймете, что мы возвращаем m_showDC - т.е. сформированный к показу на экране кадр... Далее опишем работу с приватными переменными класса: Код: public: void SetNotStarted() {IsStart = false;} void SetLevel(unsigned int lv) { Level = lv; } bool IsStarted(void){ return IsStart;} void SetStarted() {IsStart = true;} int GetErrorSubject() { return Error_subject;} unsigned int GetTimeOut() { return Timeout;} void SetTimeOut(unsigned int to) {Timeout = to;}; void IncLevel(){Level++;}; unsigned int GetLevel(void){return Level;}; unsigned int GetPoints(){return Points;} void SetPoints(unsigned int point) {Points = point;} Это стандартные методы, которые нам понадобятся для работы с переменными класса игры - т.е. с данными уже самого алгоритма для пользователя... Мы получили объект, который умеет проинициализировать данные и подготовить графику. Поставить в начальное состояние все рабочие переменные. О! Вот как раз этого мы пока не сделали... Впишем в ATetris::ATetris(void) следующие строки: Код: IsStart = false; Error_subject = 0; Points = 0; Timeout = STARTTIMEOUT; Level = 0; Теперь мы получили и стартовые значения. Кроме некоторых. Но надо еще дописать STARTTIMEOUT в ATetris.h Код: #define STARTTIMEOUT 1000 // стартовое значение таймаута #define STOPTIMEOUT 10 // минимальное значение таймаута #define POINTTOLINE 10 // количество очков за одну убранную строку Все, кажется, теперь можно приступать к алгоритму непосредственно, и в частности, к первому пункту в списке - старту игры. Для этого надо написать частный случай этого, добавление нового элемента. Добавим в ATetris Код: bool SetNewBlock(int block_code); bool ATetris::SetNewBlock(int block_code) { int x,y; PTRIS_BLOCK tb; int i,j; Error_subject = 0; current_block = block_code; if (block_code < 0) block_code = 0; if (block_code > 6) block_code = 6; current_position = 0; tb = pBlock[block_code]->GetBlock(); start_x = 2; start_y = 0; // здесь мы выполняем проверку - можем ли мы положить весь объект целиком for(i=0; i<4; i++) { for (j=0; j<4; j++) { // if Place not 0 if (tb->block[current_position][i][j] + m_Place.GetPlace(j+start_x,i+start_y) > 1) { Error_subject = NEW_FULL; return FALSE; } } } x=start_x; y=start_y; // собственно, установка элемента в массив поля. for (int i=0; i<4; i++) { for (j=0; j<4; j++) { if (tb->block[current_position][i][j] == 1) m_Place.SetPlace(x+j,y+i,tb->block[current_position][i][j]); } } return TRUE; } Давайте немного поподробнее об алгоритме - хоть это и не цель и не лучший алгоритм, но все же. Итак, при выполнении вложения нового элемента мы ничего не рисуем - как уже говорилось, мы формируем данные. В качестве данных у нас выступает стакан поля, которое надо заполнить. В частном случае начала игры никаких проверок не понадобится, но мы же пишем общий случай, поэтому первым делом после установки новых элементов на поле игры проверим, можем ли мы это сделать и не занято ли наше место на поле. Если занято, то мы уже проиграли. Так как по договоренности у нас пустое значение ячейки поля равно нулю, а неиспользуемые места в блоке тоже равны нулю, то строка проверки выглядит так: Код: if (tb->block[current_position][i][j] + m_Place.GetPlace(j+start_x,i+start_y) > 1) Это говорит о том, что любое значение одного из объектов сравнения, отличное от нуля, т.е. используемое или занятое, выдаст в сумме 2 или 3, если мы попытаемся сделать вложение в уже занятые места наш новый блок. Сама установка массива в поле Код: for (int i=0; i<4; i++) { for (j=0; j<4; j++) { if (tb->block[current_position][i][j] == 1) m_Place.SetPlace(x+j,y+i,tb->block[current_position][i][j]); } } Это в уже проверенное место полю присваиваются значения блока из данной позиции... Позиция, как вы помните, это степень поворота блока. Таким образом, иметь образ блока в объекте плюс готовое поле и значения текущей позиции блока на поле достаточно, чтобы отследить, как ведет себя блок в дальнейшем. Все достаточно просто, как видите. Если вы попытаетесь откомпилировать приложение сейчас - у вас будет как минимум одна ошибка... Даже если все сделано правильно и ресурсы уже добавлены. Это ошибка на неизвестном значении NEW_FULL. Это значение переменный ErrorSubjct которая служит для возрата значений, когда функция осуществляющая действие алгоритма это действие вполнить успешно не смогла. Все ее значения надо занести в ATetris.h Код: #define DOWN_LINE_OUT 1 #define DOWN_FULL 2 #define RIGHT_LINE_OUT 3 #define RIGHT_FULL 4 #define LEFT_LINE_OUT 5 #define LEFT_FULL 6 #define ROTATE_FULL 7 #define NEW_FULL 8 #define ERROR_OF_DATA 9 NEW_FULL - означает проигрыш игры, ведь мы заполнили стакан целиком. DOWN_FULL - будет говорить о необходимости посмотреть наличие полных линий и стереть их, а также выложить новый элемент. RIGHT_LINE_OUT - показывает, что мы, передвигая вправо падающий блок, достигли стенки станкана, и т.д. И только одна ошибка, - это ошибка серьезная - которая у меня в игре пока не возникла, но обработать ее мы обязаны: ERROR_OF_DATA - говорит об ошибке в используемых данных, а не в алгоритме. Например, неправильный номер поворота вне диапазона 0-3 или, что мы имеем отрицательный индекс в массиве поля. Итак, новый элемент мы добавили, давайте пойдем дальше и начнем организовывать уже полный алгоритм с участием самой программы, так как мы вполне можем проверить нашу работу прямо сейчас. 4.1 Первый вид Для работы с окном мы можем выбрать из двух классов, создаваемых Wizard-ом от VC++ - CMainFrm и CChildView. Естественно, что для отображения визуальной информации мы выберем второй, отвечающий за работу с визуальным отображением. Давайте подключим уже имеющийся у нас код к нашей программе непосредственно. В childview.h напишем: Код: #include "atetris.h" private: ATetris m_Tetr; Сконcтруировав объект, проинициализируем наши данные... В BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs) добавляем Код: if (!m_Tetr.Init()) { AfxMessageBox("Init Memory error!"); return FALSE; } Для старта нашей игры создадим пункт в меню Start->New game и функцию-обработчик для него. В этой функции выполним стартовые действия, которые положат старт нашей игре. Код: m_Tetr.SetPoints(0); m_Tetr.SetLevel(0); m_Tetr.CreateBaseTetris(this->GetDC()); m_Tetr.SetStarted(); Invalidate(); Т.е., разъясню. Изначально по старту программы будет вызвана функция Init, которая проинициализирует все объекты. Потом мы выполним дополнительную инициализацию уже для элементов, которые инициализируются не в старте программы, а при старте каждой игры: очки, уровень, флаг, стартовала ли игра, и создадим так называемый базовый DC, который будет готов к формированию кадров. Затем в OnPaintDC() введем Код: pDC = m_Tetr.GetTetrisDC(); if (pDC) dc.BitBlt(0,0,MAXX*PIXFORRECT,MAXY*PIXFORRECT,pDC,0,0,SRCCOPY); Эти строки сделают следующее: мы вытащим из объекта уже готовый базовый кадр текущего положения и нарисуем его. Вернее, сделаем блит на текущий контекст. Осталась самая малость... Заставить эти самые кадры формироваться в зависимости от тайм-аута. Создадим обработчик сообщения WM_TIMER. А в объекте ATetris нам понадобится функция, которую я назвал Go. По причине того, что она, собственно, главная... Код: void Go(); void ATetris::Go() { m_Place.CreateGameDC(); } Эта функция из той же прослойки, которая создается с помощью ATetris. Впишем ее вызов в OnTimer() и сынициируем работу таймера в начале игры... SetTimer(1,1000,NULL) один раз в секунду. Код: OnTimer() { m_Tetr.Go(); Invalidate(); } Вызов Invalidate() необходим для того, чтобы мы могли увидеть результаты работы приложения на экране. Теперь, запустив приложение, мы можем видеть отображение на нем битмапки поля, которое отбражается в левом верхнем углу программы. Гром




Комментарии отсутствуют
дополнительно
рассылки
опрос

Как Вы нашли наш сайт?

Через поисковик
По ссылке с другого сайта
По рекомендации
Не помню


Результаты
Все опросы

канал IRC
Внимание:
Действует чат на канале IRC.
Для подключения используйте следующие настройки:
Сервер - irc.rinet.ru или irc.baikal.net
Порт - 6669.
Канал - #Shelek.
© "Весельчак У"
email:club@shelek.com