要在游戲開(kāi)發(fā)最初清楚地知道需要做些什么,就必須構(gòu)造程序的操作流,并確保能夠很容易地對(duì)程序進(jìn)行修改。
一個(gè)典型的程序,首先要初始化所有的系統(tǒng)和數(shù)據(jù),然后進(jìn)入主循環(huán),大部分事情都出現(xiàn)在主循環(huán)中,根據(jù)游戲狀態(tài)(標(biāo)題畫(huà)面,菜單畫(huà)面,游戲畫(huà)面等),需要對(duì)輸入和輸出進(jìn)行不同的處理。
下面是一個(gè)標(biāo)準(zhǔn)游戲應(yīng)用程序應(yīng)該遵循的步驟:
1、初始化系統(tǒng)(Windows,圖形,輸入等)。
2、準(zhǔn)備數(shù)據(jù)(加載配置文件)。
3、配置缺省狀態(tài)(一般是標(biāo)題畫(huà)面)。
4、開(kāi)始主循環(huán)。
5、判定狀態(tài)并通過(guò)獲取輸入、輸出并對(duì)之進(jìn)行處理。
6、如果應(yīng)用程序沒(méi)結(jié)束,則返回第5步,否則進(jìn)行第7步。
7、清除數(shù)據(jù)(釋放內(nèi)存資源等)。
8、釋放系統(tǒng)(Windows,圖形,輸入等)。
狀態(tài)處理機(jī)的實(shí)現(xiàn)狀態(tài)是運(yùn)行狀態(tài)的簡(jiǎn)稱(chēng),表示正在運(yùn)行的應(yīng)用程序所包含的當(dāng)前處理過(guò)程。
基于狀態(tài)的編程(state-based programming)本質(zhì)上是根據(jù)狀態(tài)的一個(gè)棧分支執(zhí)行,每個(gè)狀態(tài)表示一個(gè)對(duì)象或函數(shù)集,如果需要添加函數(shù),只需要將它們加入到棧中。當(dāng)用完函數(shù)后,再?gòu)臈V幸瞥羲鼈儭?br>
使用狀態(tài)管理器可以添加刪除處理狀態(tài),當(dāng)添加一個(gè)狀態(tài)時(shí),這個(gè)狀態(tài)就被壓入棧中,這樣當(dāng)管理器還在處理時(shí),就獲得了當(dāng)前的控制權(quán),一旦狀態(tài)被彈出棧,最上面的狀態(tài)就被丟棄了,接著就會(huì)處理第二高的狀態(tài)。
如下如所示:這是一個(gè)可以在需要時(shí)將狀態(tài)壓入(push)和彈出(pop)的棧
因此需要實(shí)現(xiàn)一個(gè)接收指向函數(shù)(此函數(shù)表示狀態(tài))指針的狀態(tài)管理器,壓入一個(gè)狀態(tài)也就變成了將函數(shù)指針加入到棧中,調(diào)用狀態(tài)管理器就會(huì)處理?xiàng)V凶钌厦娴臓顟B(tài)。
以下是一個(gè)簡(jiǎn)單的實(shí)現(xiàn):
/*******************************************************************************
PURPOSE:
State-based processing Demo.
*******************************************************************************/
#include <windows.h>
#include <stdio.h>
class STATE_MANAGER
{
private:
typedef void (*STATE_FUNC_PTR)();
// A structure that stores a function pointer and linker list
struct STATE
{
STATE_FUNC_PTR func;
STATE* next;
};
protected:
STATE* _state_parent; // the top state in the stack (the head of the stack)
public:
STATE_MANAGER()
{
_state_parent = NULL;
}
~STATE_MANAGER()
{
STATE* state_ptr;
// remove all states from the stack
while((state_ptr = _state_parent) != NULL)
{
_state_parent = state_ptr->next;
delete state_ptr;
}
}
// push a function on to the stack
void Push(STATE_FUNC_PTR func)
{
// do not push a null value
if(func == NULL)
return;
// allocate a new state and push it on stack
STATE* state_ptr = new STATE;
state_ptr->next = _state_parent;
state_ptr->func = func;
_state_parent = state_ptr;
}
BOOL Pop()
{
STATE* state_ptr = _state_parent;
// remove the head of statck (if any)
if(state_ptr != NULL)
{
_state_parent = state_ptr->next;
delete state_ptr;
}
// return TRUE if more states exist, FALSE otherwise.
return (_state_parent != NULL);
}
BOOL Process()
{
// return an error if no more states
if(_state_parent == NULL)
return FALSE;
// process the top-most state (if any)
_state_parent->func();
return TRUE;
}
};
STATE_MANAGER g_state_manager;
// macro to ease the use of MessageBox function
#define MB(s) MessageBox(NULL, s, s, MB_OK);
// state function prototypes - must follow this protytype!
void Func1()
{
MB("1");
g_state_manager.Pop();
}
void Func2()
{
MB("2");
g_state_manager.Pop();
}
void Func3()
{
MB("3");
g_state_manager.Pop();
}
int PASCAL WinMain(HINSTANCE inst, HINSTANCE, LPSTR cmd_line, int cmd_show)
{
g_state_manager.Push(Func1);
g_state_manager.Push(Func2);
g_state_manager.Push(Func3);
while(g_state_manager.Process() == TRUE)
;
return 0;
}
進(jìn)程管理器的實(shí)現(xiàn)如果正在使用單獨(dú)的模塊來(lái)處理所有的中間函數(shù)(稱(chēng)為進(jìn)程),比如輸入,網(wǎng)絡(luò)以及聲音處理過(guò)程,而不是一個(gè)一個(gè)單獨(dú)調(diào)用,就可以創(chuàng)建一個(gè)對(duì)象來(lái)一次全部處理。
下圖表示一個(gè)由頻繁調(diào)用的函數(shù)組成的進(jìn)程棧,當(dāng)調(diào)用時(shí),添加到管理器的每個(gè)函數(shù)都是按序執(zhí)行的。

如下是一個(gè)簡(jiǎn)單的實(shí)現(xiàn):
/*******************************************************************************
PURPOSE:
Stack Process Demo.
*******************************************************************************/
#include <windows.h>
#include <stdio.h>
class PROCESS_MANAGER
{
private:
typedef void (*PROCESS_FUNC_PTR)();
// A structure that stores a function pointer and linked list
struct PROCESS
{
PROCESS_FUNC_PTR func;
PROCESS* next;
};
protected:
PROCESS* process_parent; // the top process in the stack (the head of the stack)
public:
PROCESS_MANAGER()
{
process_parent = NULL;
}
~PROCESS_MANAGER()
{
PROCESS* process_ptr;
// remove all processes from the stack
while((process_ptr = process_parent) != NULL)
{
process_parent = process_ptr->next;
delete process_ptr;
}
}
// add function on to the stack
void Add(PROCESS_FUNC_PTR process)
{
// do not push a NULL value
if(process == NULL)
return;
// allocate a new process and push it onto stack
PROCESS* process_ptr = new PROCESS;
process_ptr->func = process;
process_ptr->next = process_parent;
process_parent = process_ptr;
}
// process all functions
void Process()
{
PROCESS* process_ptr = process_parent;
while(process_ptr != NULL)
{
process_ptr->func();
process_ptr = process_ptr->next;
}
}
};
PROCESS_MANAGER g_process_manager;
// Macro to ease the use of MessageBox function
#define MB(s) MessageBox(NULL, s, s, MB_OK);
// Processfunction prototypes - must follow this prototype!
void Func1() { MB("1"); }
void Func2() { MB("2"); }
void Func3() { MB("3"); }
int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR cmdLine, int cmdShow)
{
g_process_manager.Add(Func1);
g_process_manager.Add(Func2);
g_process_manager.Add(Func3);
g_process_manager.Process();
g_process_manager.Process();
return 0;
}
這個(gè)對(duì)象
PROCESS_MANAGER類(lèi)似于STATE_MANAGER,但有點(diǎn)不同,PROCESS_MANAGER只添加進(jìn)程而不刪除它們。
數(shù)據(jù)包系統(tǒng)的實(shí)現(xiàn)
處理應(yīng)用程序數(shù)據(jù),最容易的方法就是創(chuàng)建一個(gè)數(shù)據(jù)包系統(tǒng),來(lái)處理保存和加載數(shù)據(jù)的工作。通過(guò)創(chuàng)建一個(gè)包含數(shù)據(jù)緩沖區(qū)的對(duì)象,就能添加一個(gè)新函數(shù)來(lái)保存和加載數(shù)據(jù)。
下圖形象的給出了數(shù)據(jù)存儲(chǔ)的方式,因?yàn)閿?shù)據(jù)緩沖區(qū)足夠大,可以存儲(chǔ)每個(gè)人名實(shí)例。在這種情況下,存儲(chǔ)了兩個(gè)名字,每個(gè)名字使用32字節(jié),加起來(lái)一共使用了64字節(jié)大小的緩沖區(qū)。

下面是一個(gè)簡(jiǎn)單的實(shí)現(xiàn):
/*******************************************************************************
PURPOSE:
Data Storage Demo.
*******************************************************************************/
#include <windows.h>
#include <stdio.h>
#pragma warning(disable : 4996)
class DATA_PACKAGE
{
protected:
// data buffer and size
void* _buf;
unsigned long _size;
public:
DATA_PACKAGE()
{
_buf = NULL;
_size = 0;
}
~DATA_PACKAGE()
{
Free();
}
void* Create(unsigned long size)
{
// free a previous created buffer
Free();
// allocate some memory and return a pointer
_size = size;
if((_buf = (void*) new char[size]) == NULL)
return NULL;
memset(_buf, 0, size);
return _buf;
}
// free the allocated memory
void Free()
{
delete _buf; _buf = NULL;
_size = 0;
}
BOOL Save(const char* filename)
{
FILE* fp;
// make sure there is something to write
if(_buf == NULL || _size == 0)
return FALSE;
// open file, write size and data.
if((fp = fopen(filename, "wb")) == NULL)
return FALSE;
fwrite(&_size, 1, 4, fp);
fwrite(_buf, 1, _size, fp);
fclose(fp);
return TRUE;
}
void* Load(const char* filename, unsigned long* size)
{
FILE* fp;
// free a prior buffer
Free();
if((fp = fopen(filename, "rb")) == NULL)
return NULL;
// read in size and data
fread(&_size, 1, 4, fp);
if((_buf = (void*) new char[_size]) == NULL)
return NULL;
fread(_buf, 1, _size, fp);
fclose(fp);
// store size to return
if(size != NULL)
*size = _size;
// return pointer
return _buf;
}
};
// a structure to contain a name
struct NAME
{
char name[32];
};
int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR cmdLine, int cmdShow)
{
DATA_PACKAGE data_package;
DWORD size;
// create the data package (w/64 bytes) and get the pointer, casting it to an NAME structure type.
NAME* names = (NAME*) data_package.Create(64);
// since there are 64 bytes total, and each name uses 32 bytes, then I can have 2 names stored.
strcpy(names[0].name, "Jim");
strcpy(names[1].name, "Adams");
const char* filename = "names.data";
// save the names to disk
data_package.Save(filename);
// load the names from disk, size will equal 64 when the load function returns.
names = (NAME*) data_package.Load(filename, &size);
// display the names
MessageBox(NULL, names[0].name, "1st name", MB_OK);
MessageBox(NULL, names[1].name, "2nd name", MB_OK);
return 0;
}
程序框架的實(shí)現(xiàn)從最基本的層面而言,框架應(yīng)該包含初始化應(yīng)用程序窗口的代碼,各種引擎(圖形,輸入,網(wǎng)絡(luò),聲音),處理初始化,每幀渲染,以及退出清理函數(shù)。
以下是一個(gè)簡(jiǎn)單的實(shí)現(xiàn):
/*****************************************************************************
PURPOSE:
Shell Application.
*****************************************************************************/
#include <windows.h>
#include <stdio.h>
#define WINDOW_WIDTH 400
#define WINDOW_HEIGHT 400
// window handles, class and caption text.
HWND g_hwnd;
HINSTANCE g_inst;
static char g_classname[] = "ShellClass";
static char g_caption[] = "Shell Application";
long FAR PASCAL Window_Proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
BOOL Do_Init();
BOOL Do_Shutdown();
BOOL Do_Frame();
int PASCAL WinMain(HINSTANCE inst, HINSTANCE, LPSTR cmd_line, int cmd_show)
{
WNDCLASSEX wnd_class;
MSG msg;
g_inst = inst;
// create the window class here and register it
wnd_class.cbSize = sizeof(wnd_class);
wnd_class.style = CS_CLASSDC;
wnd_class.lpfnWndProc = Window_Proc;
wnd_class.cbClsExtra = 0;
wnd_class.cbWndExtra = 0;
wnd_class.hInstance = inst;
wnd_class.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wnd_class.hCursor = LoadCursor(NULL, IDC_ARROW);
wnd_class.hbrBackground = NULL;
wnd_class.lpszMenuName = NULL;
wnd_class.lpszClassName = g_classname;
wnd_class.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(! RegisterClassEx(&wnd_class))
return FALSE;
// create the main window
g_hwnd = CreateWindow(g_classname, g_caption, WS_CAPTION | WS_SYSMENU,
0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL, inst, NULL);
if(! g_hwnd)
return FALSE;
ShowWindow(g_hwnd, SW_NORMAL);
UpdateWindow(g_hwnd);
// run init function and return on error
if(! Do_Init())
return FALSE;
// start message pump, waiting for singal to quit.
ZeroMemory(&msg, sizeof(MSG));
while(msg.message != WM_QUIT)
{
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if(! Do_Frame())
break;
}
// run shutdown function
Do_Shutdown();
UnregisterClass(g_classname, inst);
return int(msg.wParam);
}
long FAR PASCAL Window_Proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return long(DefWindowProc(hWnd, msg, wParam, lParam));
}
BOOL Do_Init()
{
return TRUE;
}
BOOL Do_Shutdown()
{
return TRUE;
}
BOOL Do_Frame()
{
return TRUE;
}