Разработка игр ОТ иСтатьи

Mortal Kombat's World. Урок 1: Создание приложения

Автор:

Mortal Kombat’s World

Урок 1: «Создание приложения, подключение движка, подключение плагинов движка и настройка устройств»

Оригинал статьи и исхоники на VC++ лежат на сайте http://www.ubest.net63.net

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

Разработку нашей игры начнем с того, что создадим проект, в который добавим исходные тексты и заголовочные файлы из папки Common движка.

Теперь в проект добавляем новые файлы: «MKsW.h» и «MKsW.cpp»

В файле MKsW.h объявим определение вида:

 #define WINDOW_CLASSNAME "MKsW\0"

Это определение будет использоваться в качестве имени класса и приложения. Здесь же объявим функцию обработки сообщений окна:

LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam );

В cpp-файле подключаем все необходимые заголовочные файлы:

#include <windows.h>
#include <AntGER.h>
#pragma hdrstop

#include <Common\\AntWindow.h>

#pragma comment (lib, "AntGER.lib")

#include "MKsW.h"
#include "gamevar.h"
#include "MK_Device.h"

Как вы видите, здесь я подключаю не только заголовки библиотек, но и библиотеку движка. Хотя библиотеку можно включить в линковку и штатными средствами Си. Последние 3 заголовочных файла – это файлы из нашего проекта. Последние 2 из них мы рассмотрим ниже, а пока продолжаем разработку MKsW.cpp…

Далее объявляем 2 глобальные переменные:

1.      int iGameState; - эта переменная будет использоваться для определения текущего процесса приложения (состояния игры).

2.      bool bKey[ 256 ]; - данный массив мы будем использовать для запоминания и контроля нажатых кнопок.

Для создания окна приложения будем использовать класс из AntGER Commons, для чего регистрируем класс:

CANTWINDOW cApp;

Ну и, исходя из того факта, что мы делаем игру, а любая игра должна содержать графику, необходимо обеспечить возможность загружать изображения (в нашем случае спрайты) из нужных нам форматов файлов. Для этого будем подключать требуемые dpIPI плагины:

cANTGER_PlugIns  cPlg;

Теперь можно перейти к написанию приложения.

/* Запуск приложения */
int WINAPI WinMain(     HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd )
{

Для того, чтобы иметь возможность отлавливать «глюки» в приложении мы используем логгер:

      /* Пробуем врубить логгер */
      if( !ANTGER_DEBUG_Init( "debug.htm", defANTGER_DEBUGMODE_NEW | defANTGER_DEBUGMODE_HTML ) )
            MessageBox( NULL, "Debug file can't be create!", "ERROR!", MB_ICONEXCLAMATION | MB_OK );

      ANTGER_DEBUG_Println( "<h2>MKsW Debug logging file</h2>" );
      ANTGER_DEBUG_Println( "<hr size=1>" );

Для удобства просмотра лога выполнения программы мы создаем лог-файл формата HTML. Это удобно следующими факторами: можно выделять строки так, как нравится; ошибки будут выделяться красным цветом.

Последние две команды создают заголовочную надпись (шапку) и горизонтальную линию под ней для отделения шапки от остальной части текста.

Далее в тексте я буду опускать функции лога – они есть в исходниках, а здесь их писать не к чему.

Теперь настало время создать окно приложения. Или, по крайней мере, попытаемся это сделать…

      RECT  rct;

      /* Размеры окна */
      SetRect( &rct, 0, 0, MK_SCR_W, MK_SCR_H );

      /* Регистрируем обработчик сообщений приложения */
      cApp.wcex.lpfnWndProc = WndProc;

      /* Пробуем создать окно */
      if( !cApp.CreateApp( WINDOW_CLASSNAME, hInstance, true, rct ) )
      {
            PostQuitMessage( 0 );
            return -1;
     }

Параметры MK_SCR_W и MK_SCR_H берутся из заголовочного файла «gamevar.h». Его описание я здесь не веду, т.к. там обычные объявления определений и ни каких важных моментов, имеющих место быть объясненными – нет.

Возможность регистрировать собственный обработчик сообщений приложения – это очень удобная фишка, позволяющая создавать более гибкие приложения за счет написания своего обработчика, вместо использования какого-то шаблонного, в котором может оказаться много не нужного или просто написанного не так, как это требуется нам.

Теперь о процедуре cApp.CreateApp(…):

Здесь все довольно просто. Первый параметр – это имя класса и окна приложения (ее мы определяли в файле «MKsW.h».) Второй объяснений не требует вообще. Третий – это какого типа окно будем создавать: если TRUE, то для полноэкранного режима (без шапки, бордюров и т.п.), если FALSE, то оконного типа – с шапкой, рамкой, системным меню и т.д.

Теперь подключим плагины импорта изображений. Те самые dpIPI, о которых шла речь выше. Эти плагины можно взять на нашем сайте www.dpcorp.net63.net.

cPlg.SearchAndAttachImageImportPlugins( "PlugIns" );

Как можно понять из написанного, плагины будут искаться и подключаться к движку автоматически из папки «PlugIns», расположенной в рабочей папке приложения. Ошибок мы здесь не проверяем по следующей причине – если какой-то плагин «тупит», то движок его просто не подключит. И все будет сведено к следующему – «может он и не нужен совсем?!». Если надо – проверяйте. Для этого читайте доки по движку.

Следующий шаг самый ответственный – настала пора настраивать все необходимые устройства – графические и не очень.

Вызываем соответствующую функцию:

      if( !MK_Init() )
      {
            cApp.Destroy();
            PostQuitMessage( 0 );
            return -1;
     }

В случае возникновения ошибки вызываем функцию Destroy() класса CANTWINDOW для уничтожение и дерегистрации окна.

Функция MK_Init у нас будет находится в файле «MK_Device.cpp». Ее объявление в одноименном заголовочном файле.

В этом файле зарегистрируем класс графического устройства и звукового устройства:

CANTGRAPHIC       cGrf;       // Класс графики
cANTGER_Sound     cSnd;       // Класс звукового устройства

Класс графики у нас используется не из движка, а из Commons, чтобы упростить некоторую часть разработки. В самой функции MK_Init() сначала настраиваем звуковое устройство, потому что это действие требует меньше затрат по времени и нагрузки на систему, чем настройка графического устройства и, к тому же, если не настроится звук, то и графика нам уже, собственно говоря, не к чему.

      /* Настраиваем звуковое устройство */
      if( cSnd.Init( cApp.hWnd ) != ANTGER_ERROR_OK )
           return false;

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

      /* Получим наилучшую герцовку для тек. разрешения, но не более 90 Гц */
      int   iHZ = cGrf.GetMaxHZ( MK_SCR_W, MK_SCR_H, 32, 90 );

      /* Настраиваем графическое устройство */
      if( !cGrf.Init( cApp.hWnd, MK_SCR_W, MK_SCR_H, 32, iHZ ) )
           return false;

После того, как мы обеспечили настройку устройств, можно продолжать разработку кода. Установим игру в состояние «игровой процесс»:

iGameState = GState_Play;

Организовываем цикл приложения:

      /* Циклимся, пока не будет команда выйти из процесса */
      while( iGameState != GState_End )
      {
            /* Обработка стандартных оконных сообщений */
            while( PeekMessage( &message, NULL, 0, 0, PM_NOREMOVE ) )
            {
                  if( GetMessage( &message, NULL, 0, 0 ) )
                  {
                        TranslateMessage( &message );
                        DispatchMessage( &message );
                  }
                  else
                        return 0;
            }

            /* Обработка процессов */
            switch( iGameState )
            {
                  /* Процесс игры */
                  case GState_Play:
                        break;
            } // switch( Game_State )
     } // while ...

Описанный выше код, я так думаю, пояснений не требует. Разработка обработчика оконных сообщений тоже дело штатное, поэтому я акцентируюсь лишь на трех его моментах:

1. Если подана команда «закрыть окно» или «разрушить окно»:

      case WM_CLOSE:
      case WM_DESTROY:
            {
                  /* Освобождаем занятые устройства */
                  MK_Uninit();

                  /* Закрываем лог-файл */
                  ANTGER_DEBUG_Close();

                  /* Вырубаем приложение */
                  PostQuitMessage( 0 );
            }
           break;

Описание функции MK_Uninit я не привожу по той причине, что там всего 2 строчки. Их можете посмотреть в исходниках – там все предельно ясно.

2. Если была нажата клавиша:

      /* Регистрируем нажатие кнопки */
      case WM_KEYDOWN:
            bKey[ wParam ] = true;
           break;

3. Если клавиша была отпущена:

      case WM_KEYUP:
            bKey[ wParam ] = false;
            switch( wParam )
            {
                  /* Если это был Escape, то закрываем приложение */
                  case VK_ESCAPE:
                        SendMessage( hWnd, WM_DESTROY, 0, 0 );
                        break;
            }
           break;

Обработка клавиши «Escape» ведется в случае освобождения клавиши потому, чтобы ее нажатие не было случайно зафиксировано системой.

На этом первый урок закончен – приложение подготовлено. Оно может настраивать необходимые устройства и выключаться по нажатию клавиши «Escape», а так же ведет лог всех событий, которые мы учитываем.

В следующем уроке мы продолжим разработку игры и уже начнем использовать графические ресурсы.

X-Ray Spider

Copyright ©2010

13 марта 2010

Комментарии [4]