Программирование стратегических игр с DirectX 9.0

         

Файл программы Main.cpp


Файл main.cpp не слишком сложен, поскольку он по большей части следует каркасу приложения DirectX. Первая представляющая для нас интерес функция — это конструктор класса. Вот его код:

CD3DFramework::CD3DFramework() { m_strWindowTitle = _T("2D Tile Example"); m_pStatsFont = NULL; m_shWindowWidth = 480; m_shWindowHeight = 480; m_shTileMapWidth = 10; m_shTileMapHeight = 10; }

Как видно из приведенного фрагмента кода, в конструкторе класса задается размер блочной карты. Я установил высоту и ширину карты равной 10 блокам, чтобы при выводе карта занимала все окно целиком. Ширина и высота блока равны 48 пикселам, поэтому размер окна устанавливается равным 480 на 480 точек. Поскольку 10 * 48 = 480, данный размер как раз обеспечивает точное совпадение окна и выводимой карты.

Следующий фрагмент кода, который представляет интерес, выполняет инициализацию блочной карты.

HRESULT CD3DFramework::OneTimeSceneInit() { m_pStatsFont = new CD3DFont(_T("Arial"), 8, NULL); if(m_pStatsFont == NULL) return E_FAIL; // Заполнение карты блоками с изображением травы memset(m_iTileMap, 0, (m_shTileMapWidth * m_shTileMapHeight) * sizeof(int)); // заполнение второй половины блоками с изображением песка for(int i = 0; i < 50; i++) { m_iTileMap[i+50] = 3; } // Случайное размещение камней на траве // Инициализация генератора случайных чисел srand(timeGetTime()); for(i = 0; i < 50; i++) { // Размещение камней на траве, если случайное число = 5 if(rand()%10 == 5) m_iTileMap[i] = 1; } // Размещение переходных блоков между травой и песком for(i = 50; i < 60; i++) { m_iTileMap[i] = 2; }

return S_OK; }

Изучать этот код мы начнем со строки в которой находится вызов функции memset(). Данный фрагмент очищает блочную карту, присваивая всем ее элементам значение 0. Текстура с номером 0 содержит изображение травы, так что рассматриваемый код заполняет травой всю карту.

Следующая часть кода представляет собой цикл, в котором блокам в нижней половине карты присваивается значение 3. Блок с номером 3 содержит текстуру с изображением песка; таким образом этот код формирует песчанный пляж в нижней части карты.


Загрузите файл main.cpp. Первая вещь, которая представляет для нас интерес — глобальная переменная с именем g_iNumTextures. Она содержит количество загружаемых в память текстур блоков. Я создал ее, чтобы упростить добавление блоков в программе. В настоящее время значение этой переменной равно 9. Если вы будете экспериментировать с программой и добавите свои собственные блоки, убедитесь, что соответствующим образом изменено и значение переменной.

Переместимся далее, к коду конструктора класса. Вы видите, что в нем присваиваются значения нескольким переменным класса:

m_shWindowWidth = 640; m_shWindowHeight = 320; m_shTileMapWidth = 10; m_shTileMapHeight = 10;

На этот раз я создаю окно, ширина которого равна 640 точкам, а высота — 320 точкам. Я поступаю так по той причине, что визуализация изометрических блоков слегка отличается от визуализации двухмерных квадратных блоков, и для нее требуется большая экранная область.

Следующий блок переменных задает размеры визуализируемой блочной карты. Если вы решите увеличить размер блочной карты, не забудьте добавить элементы к массиву m_iTileMap.

Теперь взглянем на функцию класса OneTimeSceneInit(). В этой функции я заполняю два слоя блочной карты данными, после того, как очищу массив функцией memset().

// Инициализация генератора случайных чисел srand(timeGetTime()); for(int i = 0; i < 100; i++) { // Заполнение базового слоя блоками с изображением травы песка и брусчатки if(rand()%10 == 3) m_iTileMap[i][0] = 2; else if(rand()%10 == 4) m_iTileMap[i][0] = 3; else m_iTileMap[i][0] = 4; // Заполнение слоя деталей деревьями и колоннами if(rand()%10 == 5) m_iTileMap[i][1] = 6; else if(rand()%10 == 4) m_iTileMap[i][1] = 5; else if(rand()%10 == 3) m_iTileMap[i][1] = 8; }

Сперва вызов функции srand() инициализирует генератор случайных чисел, используя в качестве начального значения текущее системное время. Чтобы получить текущее значение времени, я вызываю функцию timeGetTime(). Она находится в библиотеке winmm.lib и требует, чтобы в программу был включен заголовочный файл mmsystem.h. Инициализация генератора случайных чисел позволяет получать различные результаты при каждом запуске программы.




Первое отличие, которое вы можете заметить— отсутствие инициализации буфера вершин для визуализации блоков. Эти действия нам больше не требуются, поскольку для всех задач визуализации программа использует интерфейс спрайтов. Чтобы отображать спрайтовую графику необходимо создать спрайтовое устройство. Это делается в функции RestoreDeviceObjects() с помощью следующей строки кода:

D3DXCreateSprite(m_pd3dDevice, &pd3dxSprite);

Просто, да? Функция D3DXCreateSprite() выполнит за вас всю работу, необходимую чтобы создать спрайтовое устройство. Ей передаются два параметра. Первый параметр — это указатель на устройство трехмерной визуализации. Второй параметр является адресом указателя на спрайтовое устройство, которое будет создано функцией.

Переместимся к функции Render(), чтобы посмотреть следующий набор изменений кода. Цикл визуализации выглядит также как и раньше — один внешний цикл и один внутренний. Самые значительные отличия расположены в коде внутренннего цикла. Вместо вызова функции vDrawTile() теперь используется вызов функции BltSprite(). Еще одно отличие заключается в том, что для указания местоположения блока теперь используются прямоугольники, а не значения с плавающей точкой, определяющие координаты в трехмерном пространстве. Отметим важный момент — прямоугольники определяют местоположение на экране, а не в трехмерном пространстве.

Смещение позиции визуализации присутствует в этой программе, так же как и в предыдущих. Единственное отличие заключается в типе используемых единиц измерения. Поскольку интерфейс спрайтов работает в пространстве экранных координат, смещения слегка отличаются.

Перед тем, как начать визуализацию, вы должны вызвать функцию спрайтового устройства Begin(). Завершать визуализацию следует вызовом функции спрайтового устройства End(). Эти вызовы полностью отличаются от того, что мы делали при работе с трехмерным устройством. Они нужны только в том случае, если вы используете спрайты, и не применяются, когда используется трехмерная визуализация.




Теперь откройте файл main.cpp, чтобы увидеть код, используемый в данном примере. Ниже приведен первый фрагмент кода, который представляет для нас интерес:

HRESULT CD3DFramework::OneTimeSceneInit() { int i; m_pStatsFont = new CD3DFont(_T("Arial"), 8, NULL); if(m_pStatsFont == NULL) return E_FAIL; // Выделение памяти для блоков for(i = 0; i < g_iNumTiles; i++) { m_pObject[i] = new CD3DMesh(); } // Заполнение карты блоками с кодом 0 memset(m_iTileMap, 0, (m_shTileMapWidth * m_shTileMapHeight) * sizeof(int)); // Случайное размещение скал на траве // Инициализация генератора случайных чисел srand(timeGetTime()); for(i = 0; i < 100; i++) { if(rand() % 5 == 3) m_iTileMap[i] = 1; else m_iTileMap[i] = 0; } return S_OK; }

Новые действия начинаются в первом цикле for. В нем выделяется память для трехмерных объектов (блоков). Для этой цели используется оператор new. Смотрите, разве это не просто?

Далее расположен еще один цикл for предназначенный для случайного размещения блоков на карте. В данном примере используются всего два типа блоков, так что код должен всего лишь выбрать одни из этих двух блоков и поместить его на карту. В данном случае 1 — это блок с изображением горы, а 0 — блок с изображением травы.



Содержание раздела