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

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


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


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

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



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



Советы по Windows (7.1)
Предисловие В этой части советов я решил оформить серию статей по работе с графикой в окнах Windows без использования работы DirectX и OpenGL, а только посредством стандартных функций API и MFC. Кроме того, важно понять после советов в виде отрывочных статей, как правильно планировать и писать программы под Windows с учетом всех ее особенностей. Для хорошего понимания, как всегда, я использую работу на примере. А писать в этот раз мы будем Тетрис, игрушку вечную, но до сих пор популярную. 1. Планирование данных. В первую очередь программное планирование начинается с определения и формализации задачи. Саму задачку мы определили, как Тетрис обыкновенный, и теперь понимая нашу цель можем приступить к формализации задачи. Разделим программму на данные и методы работы с ними. В качестве данных будут выступать два основных объекта игры - падающий блок и игровое поле. Создадим обычную апликацию, которая в Wizard-е от студии называется MFC Application. Обзовем ее tris. В окне Application type выберем Single Document, а птичку поддержки структуры Document/View выключим, так как работа с документом в его стандартном виде нам не пригодится. В результате мы будем иметь простое окошко с менюшкой и белым фоном, на котором ничего нет... Первым объектом данных будет класс ABlock. Определим тип данных которым этот объект будет владеть и с которым мы будем работать. Код: (cpp) typedef struct _TRIS_BLOCK { int block[4][4][4]; } TRIS_BLOCK, * PTRIS_BLOCK; Можно было бы обойтись и просто описанием массива. Но так получается более наглядно, к тому же в будущем нам может понадобиться расширить свойства блока, добавив, например, цвет. Вы спросите почему 3 уровня массива. И я вам отвечу - мы имеем дело с выбором между избыточностью данных и избыточностью (сложностью) кода. Каждый блок падая, поворачивается на 90 градусов. Мы имеем выбор - либо взять и написать алгоритм поворота объекта, либо держать в данных все 4 его положения, при повороте манипулируя его индексами в массиве. Я выбрал второй путь, так как алгоритм поворота мне писать лень :) Сам класс блок получился очень простым: Код: (cpp) class ABlock { public: ABlock(TRIS_BLOCK ptb); ~ABlock(void); PTRIS_BLOCK GetBlock(); private: TRIS_BLOCK tb; }; В файле ABlock.cpp пропишем содержимое 2-х функций... Код: ABlock::ABlock(TRIS_BLOCK in_tb) { memset(&tb,0,sizeof(int) * 4*4*4); memcpy(&tb,&in_tb,sizeof(int) * 4*4*4); } PTRIS_BLOCK ABlock::GetBlock() { return &tb; } Обратите внимание на то, что объект не имеет пустого конструктора типа ABlock(), потому что без данных сам по себе объект нам никогда не понадобится. На этом объект Block мы закончили и приступим к второму типу данных - поле (Place). Создадим такой - же пока пустой объект APlace, и опишем три константы: Код: (cpp) #define MAXX 10 // Ширина поля #define MAXY 20 // Высота поля #define PIXFORRECT 20 // Колличество точек на квадрат(ячейку) (в данном случае квадрат - это один элемент стакана тетриса, где может располагаться честь блока) Значения для поля изначально установим равными нулю, что означает пустой квадрат. Код: (cpp) class APlace { public: APlace(void); ~APlace(void); }; В этом объекте нам понадобятся данные - само поле. Представим поле в виде двухмерного массива ячеек: Код: (cpp) private: int Place[MAXX][MAXY]; Естественно, что в данном случае за ячейку поля мы определяем 1 квадрат. В секцию public запишем: Код: (cpp) int SetPlace(int x, int y, int set_point); int GetPlace(int x, int y); Эти функции будут использоваться для управления значениями ячеек поля. И заполним наш APlace.cpp: Код: APlace::APlace(void) { memset(Place,0,sizeof(int)*MAXX*MAXY); } APlace::~APlace(void) { } int APlace::SetPlace(int x, int y, int set_point) { if (x > MAXX) return 0xFFFF; if (y > MAXY) return 0xFFFF; if ((x<0) || (y<0)) return 0xFFFF; Place[x][y] = set_point; return 1; } int APlace::GetPlace(int x,int y) { if ( (x > MAXX) || (x < 0) || (y>MAXY) || (y <0) ) return 0xFFFF; return Place[x][y]; } Мы обеспечили минимально необходимую организацию данных, и можем перейти к разработке логики игры - т.е. непосредственно к алгоритму. Все дунные описаны и формализованы. 2. Разработка алгоритма Прежде, чем перейти к алгоритму, я бы хотел напомнить, или показать, тем кто не знает, основные принципы работы окна программы на программном уровне. Как я уже писал, программа на Windows - это обычный цикл, ждущий прихода сообщений и на них реагирующий. На деле это обычный вызов функции по появлении в очереди сообщений, данных, предназначенных текущему окну. Вызов и отработка сообщений происходит в функции DispatchMessage(); В MFC основную работу по созданию такого цикла код программы скрывает - оставляя нам макросы ON_COMMAND и ON_MESSAGE в которых с помощью wizard-а мы можем создавать функции обработчики таких сообщений. В цель этой статьи не входит рассказ где найти в студии той или иной версии VC++ те или иные сообщения. Для отработки алгоритма, всегда приходится делить действия, на происходящие по велению пользователя: работа с меню, или кнопками, запрос на установку и вычитывание данных, работа с клаиатурой и мышкой; и на те, которые программа должна делать независимо от пользователя, без его требования. В нашем случае Тетрис постоянно в момент работы делает следующие процедуры: * выставляет новый блок в верх поля, если текущий блок достиг низа, или это старт программы. * сдвигает текущий блок вниз с определенной скоростью. * проверяет на наличие заполненных строк в стакане (поле) для их уничтожения и сдвигает оставшиеся выше части вниз. * в случае невозможности выставить новый блок (стакан заполнен) принимает решение о окончании игры. Все эти действия необходимо делать постоянно. Есть для этого несколько способов. * в API структуре можно упихать всех их в постоянный цикл проверки сообщений однако, такой метод не рекомендуется, так как в более насыщенных действиями программах это будет сильно тормозить обработку сообщений. * созданием дополнительного потока (thread) в котором будут происходить все действия. Чаще применяется для работы с программмами требующими постоянной работы и слежения за потоками данных. * если действия дискретны и происходят 1 раз за постоянный промежуток времени, то всех их "сажают" на сообщение таймера (WM_TIMER). У нас действия более чем подходят под пункт "c" в котором каждое действие будет происходить по тайм-ауту, который в зависимости от сложности уровня можно уменьшать. Логика самой игры подсказывает следующее решение. Физически наши данные будут представлять из себя набор значений. Для блока при максимальной длине элемента в четыре ячейки (прямая линия) мы определили в качестве данных массив, который содержит матрицу самого элемента с значениями; 1 - блок в ячейке присутствует. 0 - ячейка блоком не используется. массив таких матриц из 4-х положений поворота составит все возможные положения блока в пространстве поля. Поле, представляющее из себя двухмерную матрицу будет получать отображение текущей матрицы элемента в виде заполненных ячеек теми же единицами. При заполнении есть возможность проверить мешает ли что-либо установить блок в текущую позицию сравнивая значения из используемых блоком ячеек с соответствующими им ячейками поля. При этом ячейки поля смогут принимать не два, а более значений, которые могут сказать нам о том, что данная ячейка принадлежит линии поля полностью заполненной, которая подлежит уничтожению, или даже о цвете данной ячейки, буде появиться такая необходимость... В любом случае, так мы универсально сможем закодировать состояние игры которое сможем отображать на экране. После того как принцип алгоритма (шаговый и по возбуждению пользователем), а так же принцип формальной записи процесса в данные стал понятен приступим к разработке самого игрового интерфейса.




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

Каким языком программирования Вы владеете?

Perl
Ассемблер
Дельфи
Язык управления 1С
Языки С & C++
Вледею всеми
Не владею ни одним


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

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