Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Programming / GameProg / RPG_Programming_2ed.pdf
Скачиваний:
240
Добавлен:
12.02.2016
Размер:
12.06 Mб
Скачать

Джим Адамс

Системное ядро

Системное ядро обрабатывает инициализацию и поток выполнения программы обычного игрового приложения Windows, а также процессы, состояния и упаковку данных. Это то место, с которого вы начинаете разработку вашего нового игрового проекта.

Использование объекта ядра cApplication

Наиболее полезный объект в системном ядре — это cApplication,

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

При работе приложения класс cApplication вызывает три

предоставляемых вами (через объявление наследуемого класса) перегруженных функции: Init, Shutdown и Frame. У каждой из

функций есть свое особое назначение, с которым вы познакомитесь позже в этом разделе. Для работы с сообщениями Windows вам надо также предоставить обработчики сообщений. Об этом я тоже расскажу чуть позже.

А теперь пойдем вперед и взглянем на приведенный ниже код, содержащий объявление класса cApplication:

class cApplication

{

private:

HINSTANCE m_hInst; // Дескриптор экземпляра

HWND m_hWnd;

// Дескриптор окна

protected:

m_Class[MAX_PATH];

// Имя класса

char

char

m_Caption[MAX_PATH];

// Заголовок окна

WNDCLASSEX m_wcex;

 

// Структура класса окна

DWORD

m_Style;

 

// Стиль окна

DWORD

m_XPos;

 

// Координата X окна

DWORD

m_YPos;

 

// Координата Y окна

DWORD

m_Width;

 

// Ширина окна по умолчанию

DWORD

m_Height;

 

// Высота окна по умолчанию

public:

 

// Конструктор

 

 

cApplication();

 

 

HWND

GethWnd();

// Возвращает дескриптор окна

HINSTANCE GethInst(); // Возвращает дескриптор экземпляра

BOOL Run(); // Исполняет код класса

 

// Печать ошибок

BOOL Error(BOOL Fatal, char *Text, ...);

BOOL Move(long XPos, long YPos);

//

Перемещение окна

BOOL Resize(long Width, long Height); //

Изменение размера

BOOL ShowMouse(BOOL Show = TRUE);

//

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

//

Скрывает или

 

 

 

//

отображает курсор

 

 

 

 

 

netlib.narod.ru

 

297

Глава 6. Создаем ядро игры

// Обработка событий по умолчанию

UINT uMsg,

virtual FAR PASCAL MsgProc(HWND hWnd,

 

WPARAM wParam, LPARAM lParam) {

return DefWindowProc(hWnd, uMsg, wParam, lParam);

}

 

 

// Пользовательские функции, содержащие код игры

virtual BOOL Init()

{ return TRUE; }

 

virtual BOOL Shutdown() { return TRUE; }

 

virtual BOOL Frame()

{ return TRUE; }

 

};

Количество вызываемых функций в классе cApplication

минимально, поскольку он разработан фактически для того, чтобы исполнять самого себя — вы просто добавляете ваш код игры.

Чтобы использовать класс cApplication вы сначала должны создать класс-наследник, использующий cApplication в качестве базового

класса. Так вы выполняете перегрузку определенных функций для

соответствия вашим собственным требованиям. Перегружаемые функции, как упоминалось ранее, — это Init, Shutdown и Frame.

Вфункции cApplication::Init вы размещаете весь код

инициализации класса, такой как загрузка данных, подготовка состояний и т.д. Противоположность Init — функция cAplication::Shutdown, которая освобождает все ранее выделенные ресурсы. Функция Shutdown

вызывается самой последней, а функция Init — самой первой.

Далее следует функция cApplication::Frame, которая вызывается почти на каждой итерации цикла обработки сообщений (в том случае, если у Windows нет сообщений для обработки). Как можно предположить, функция Frame обрабатывает один кадр из вашей игры, что может включать обработку пользовательского ввода, проверку сетевых данных и рисование графики.

ПРИМЕЧАНИЕ

Каждая из упомянутых функций (Init, Shutdown и Frame)

 

возвращает логическое значение, сообщающее классу

 

cApplication

о том, продолжать ли выполнение

 

программы или выйти из нее. Если какая-либо из этих

 

функций возвращает

FALSE, работа приложения

прерывается.

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

Windows. Для обработки сообщений вы перегружаете функцию cApplication::MsgProc, как я вскоре покажу вам.

А пока уделите минуту своего времени на работу с классом cApplication для быстрого создания приложения. Убедитесь, что не

 

забыли включить в ваш проект файлы

Core_System.h и Core_System.cpp, а

 

также унаследовали класс вашего приложения, с которым будете работать,

 

как показано ниже:

 

 

 

298

netlib.narod.ru

Джим Адамс

#include "Core_System.h"

class cApp : public cApplication

{

private:

char *m_Name;

public:

// Конструктор

cApp();

BOOL Init();

// Перегруженная функция Init

BOOL Shutdown();

// Перегруженная функция Shutdown

BOOL Frame();

// Перегруженная функция Frame

};

Этот пример кода инициализирует ваше приложение регистрируя класс окна, создавая окно и запуская конвейер сообщений, в котором непрерывно вызывается функция Frame. Цель примера — создание буфера и сохранение

в нем текста (в данном случае моего имени) и отображение этого текста в каждом кадре. Завершая работу приложения вы освобождаете текстовый буфер и выходите из программы.

cApp::cApp()

{

//Присваиваем данным экземпляра

//значения по умолчанию

m_Name = NULL;

//Устанавливаем стиль, местоположение,

//ширину и высоту окна

m_Style

= WS_OVERLAPPEDWINDOW;

// Стиль окна

m_XPos

= 100;

// Координата

X

окна

m_YPos

= 20;

// Координата

Y окна

m_Width

=

400;

// Ширина

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

m_Height

=

400;

// Высота

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

// Назначаем имя класса и заголовок окна strcpy(m_Class, "NameClass"); strcpy(m_Caption, "My Name Example");

}

BOOL cApp::Init()

{

// Выделяем память для хранения имени if((m_Name = new char[10]) == NULL) strcpy(m_Name, "Jim Adams");

else

return FALSE; return TRUE;

}

BOOL cApp::Shutdown()

{

// Освобождаем буфер имени delete [] m_Name;

m_Name = NULL; // Сброс указателя

}

BOOL cApp::Frame()

{

// Отображаем имя и ждем нажатия OK или CANCEL,

netlib.narod.ru

299

Глава 6. Создаем ядро игры

// выход из программы по CANCEL if(MessageBox(GethWnd(), m_Name, "My name is",

MB_OKCANCEL) == IDCANCEL) return FALSE;

return TRUE;

}

Ну вот! Все, что осталось сделать — это создать экземпляр вашего нового класса и запустить его из функции WinMain:

int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int nCmdShow)

{

cApp App;

return App.Run();

}

Обработка состояний с cStateManager

В главе 1, «Подготовка к работе с книгой», было показано использование класса обработки на основе состояний. В этом разделе я применю данную

информацию в качестве фундамента и покажу вам усовершенствованную версию разработанного в главе 1 класса cStateManager. При создании ваших игр вы обнаружите, что эта версия cStateManager лучше

подходит для управления потоком выполнения игровой программы.

Новый диспетчер состояний добавляет пару концепций: цели вызова и добавление к функциям в cStateManager определяемых пользователем

указателей на данные.

ПРИМЕЧАНИЕ

Цель вызова (calling purpose) — это, согласно названию,

 

причина, по которой вызывается состояние. «Целью»

 

может быть INITPURPOSE (сигнализирует, что функция

 

должна подготовить себя к работе), FRAMEPURPOSE (для

 

обработки отдельного кадра) или SHUTDOWNPURPOSE (для

 

освобождения всех ресурсов, когда обработка завершена).

Вот код объявления класса cStateManager:

class cStateManager

{

// Указатели на функции состояний (связанный список) typedef struct sState {

void (*Function)(void *Ptr, long Purpose); sState *Next;

//Конструктор структуры, очищающий указатели sState() {

Function = NULL; Next = NULL;

}

//Деструктор структуры для удаления

//из связанного списка

~sState() { delete Next; Next = NULL; } } sState;

300

netlib.narod.ru

Джим Адамс

protected:

sState *m_StateParent; // Родитель связанного списка // стека состояний

public:

cStateManager(); // Конструктор ~cStateManager(); // Деструктор

//Помещает состояние в стек вместе с определяемым

//пользователем указателем. Функция помещения в стек

//вызывает функцию состояния, указывая в качестве

//цели инициализацию

void Push(void (*Function)(void *Ptr, long Purpose), void *DataPtr = NULL);

//Выталкивание верхнего состояния из стека с вызовом

//функции состояния для завершения работы

BOOL Pop(void *DataPtr = NULL);

//Выталкивание всех состояний с завершением работы

//каждого из них

void PopAll(void *DataPtr = NULL);

//Обработка верхнего состояния в стеке с указанием

//обработки отдельного кадра в качестве цели вызова

BOOL Process(void *DataPtr = NULL);

};

Работа с классом cStateManager может сперва показаться

необычной (особенно что касается целей вызова), но не следует

беспокоиться, для этого и существуют примеры! Взгляните на пример, который базируется на предыдущем примере работы с cApplication:

class cApp : public cApplication

{

private:

cStateManager m_StateManager;

// Прототипы функций состояний static void Function1(void *, long); static void Function2(void *, long);

public:

BOOL Init() { m_StateManager.Push(Function1, this); }

};

void cApp::Function1(void *DataPtr, long Purpose)

{

//Получаем указатель на вызывающий класс,

//поскольку функция статическая, а значит

//не имеет назначенного экземпляра класса cApp *cc = (cApp*)DataPtr;

//При инициализации отображаем сообщение

//и помещаем в стек второе состояние if(Purpose == INITPURPOSE) {

MessageBox(cc->GethWnd(), "State 1", "Message", MB_OK);

cc->m_StateManager.Push(Function2, cc); return;

}

netlib.narod.ru

301

Соседние файлы в папке GameProg