В этой части советов я решил оформить серию статей по работе с графикой в окнах Windows без использования работы DirectX и OpenGL, а только посредством стандартных функций API и MFC. Кроме того, важно понять после советов в виде отрывочных статей, как правильно планировать и писать программы под Windows с учетом всех ее особенностей.
Для хорошего понимания, как всегда, я использую работу на примере. А писать в этот раз мы будем Тетрис, игрушку вечную, но до сих пор популярную.
1. Планирование данных.
В первую очередь программное планирование начинается с определения и формализации задачи. Саму задачку мы определили, как Тетрис обыкновенный, и теперь понимая нашу цель можем приступить к формализации задачи.
а) разделим программму на данные и методы работы с ними. В качестве данных будут выступать два основных объекта игры
- падающий блок и игровое поле.
Создадим обычную апликацию, которая в Wizard-е от студии называется MFC Application. Обзовем ее tris. В окне Application type выберем Single Document, а птичку поддержки структуры Document/View выключим, так как работа с документом в его стандартном виде нам не пригодится.
В результате мы будем иметь простое окошко с менюшкой и белым фоном, на котором ничего нет...
Первым объектом данных будет класс ABlock.
Определим тип данных которым этот объект будет владеть и с которым мы будем работать.
typedef struct _TRIS_BLOCK { int block[4][4][4]; } TRIS_BLOCK, * PTRIS_BLOCK;
Можно было бы обойтись и просто описанием массива. Но так получается более наглядно, к тому же в будущем нам можт понадобиться расширить свойства блока, добавив, например, цвет.
Вы спросите почему 3 уровня массива.
И я вам отвечу - мы имеем дело с выбором между избыточностью данных и избыточностью (сложностью) кода. Каждый блок падая, поворачивается на 90 градусов. Мы имеем выбор - либо взять и написать алгоритм поворота объекта, либо держать в данных все 4 его положения, при повороте манипулируя его индексами в массиве. Я выбрал второй путь, так как алгоритм поворота мне писать лень :)
Обратите
внимание на то, что объект не имеет пустого конструктора типа ABlock(), потому что без данных сам по себе объект нам никогда не понадобится.
На этом объект Block мы закончили и приступим к второму типу данных - поле (Place).
Создадим такой - же пока пустой объект APlace, и опишем три константы:
#define MAXX 10 // Ширина поля #define MAXY 20 // Высота поля
#define PIXFORRECT 20 // Колличество точек на квадрат(ячейку) (в данном случае квадрат - это один элемент стакана тетриса, где может располагаться честь блока)
Значения для поля изначально установим равными нулю, что означает пустой квадрат.
class APlace { public: APlace(void); ~APlace(void); };
В этом объекте нам понадобятся данные - само поле. Представим поле в виде двухмерного массива ячеек:
private: int
Place[MAXX][MAXY];
Естественно, что в данном случае за ячейку поля мы определяем 1 квадрат.
В секцию public запишем:
int SetPlace(int x, int y, int set_point); int GetPlace(int x, int y);
Эти функции будут использоваться для управления значениями ячеек поля. И заполним наш APlace.cpp:
Мы
обеспечили минимально необходимую организацию данных, и можем перейти к разработке логики игры - т.е. непосредственно к алгоритму. Все дунные описаны и формализованы.
Игровой интерфейс включает в себя: а) графическое отображение на экране б) пользовательское управление в) сам алгоритм игры.
Для отработки каждого действия на игровом поле в нашем случае необходимо будет работать с графическими объектами, которые в Windows вполне неплохо и понятно сделаны.
Нам понадобиться: а) отобразить игровое поле пустым в начале. б) отображать объекты типа блок (ABlock) на игровм поле. г) отображать передвижение и поворот объекта ABlock. д) отображать оставшиеся объекты в стакане в виде занятых ячеек. е) отображать процесс уничтожения объектов на поле, когда вся линия в стакане заполнена, и сдвиг всех занятых клеток вниз, после уничтожения полных строк.
Для отображения простой картинки в Windows используют так называемый Device context. В MFC предусмотрено несколько объектов
для работы с ними. Это CDC - базовый объект, CClientDC - используется для работы с клиентской частью окна (белый фон) CWindiwDC - полностью все окно, можно накладывать рисунок даже на область шапки, меню и т.д., и CPaintDC который используется программой для собственно вывода в область окна CClientDC в процедуре OnPaint.
OnPaint - процедура обработки соробщения WM_PAINT которое посылается окну каждый раз, когда система считает необходимым перерисовать окно программы (после команды минимизировать или максимизировать окно например), или когда вы вызываете функцию Invalidate();
Таким образом, для постоянного отображения графического контекста на экране, нам необходимо рисовать всю информацию в процедуре OnPaint().
Для нас, немного сложно сразу перейти к разработке полного игрового интерфейса, поэтому переместимся немного в код, и доработаем его под наши нужды...