寫(xiě)程序時(shí),處理重復(fù)(或?qū)こ#┐a的技巧就是創(chuàng)建一個(gè)有用的函數(shù)構(gòu)成的核心庫(kù),由這個(gè)核心庫(kù)來(lái)處理重復(fù)的代碼。使用這個(gè)函數(shù)庫(kù),就無(wú)需反復(fù)書(shū)寫(xiě)相同的 DirectX或Windows代碼,因而能夠幫助程序員快速創(chuàng)建游戲工程。
理解內(nèi)核概念 游戲內(nèi)核(game core)是用來(lái)簡(jiǎn)化DirectX和Windows編程的一些庫(kù)的集合。游戲內(nèi)核中幾乎含有一個(gè)游戲工程需要使用的所有函數(shù),這些函數(shù)一般用于繪制圖形、播放聲音、處理用戶(hù)輸入以及應(yīng)用程序。也就是說(shuō),使用游戲內(nèi)核,就無(wú)需在每次開(kāi)始一個(gè)新的游戲工程時(shí),再去處理底層的DirectX或Windows 代碼。
游戲內(nèi)核主要包括5個(gè)內(nèi)核:
系統(tǒng)內(nèi)核(system core) :用于Window處理過(guò)程,包括注冊(cè)窗口類(lèi),創(chuàng)建應(yīng)用程序窗口以及處理進(jìn)程、狀態(tài)和數(shù)據(jù)包。
圖形內(nèi)核(graphics core):用于繪制圖形,使用2D方法快速繪制圖像或使用諸如動(dòng)畫(huà)網(wǎng)格之類(lèi)的3D方法渲染場(chǎng)景。
輸入內(nèi)核(input core): 處理來(lái)自鍵盤(pán)、鼠標(biāo)和游戲桿的用戶(hù)輸入。
聲音內(nèi)核(sound core): 讓用戶(hù)享受多聲道的聲音和音樂(lè),此內(nèi)核能夠改變樂(lè)器聲音并帶來(lái)獨(dú)特的音樂(lè)感受。
網(wǎng)絡(luò)內(nèi)核(network core): 此內(nèi)核用于將客戶(hù)機(jī)連接到Internet以及一些大型在線(xiàn)游戲的服務(wù)器,使用此內(nèi)核,就能夠暢游網(wǎng)絡(luò)。
每個(gè)內(nèi)核都可以單獨(dú)使用,并且在一個(gè)游戲工程中沒(méi)有必要包含所有的內(nèi)核,如果只想擁有聲音特性,只包含聲音內(nèi)核即可,如果想給系統(tǒng)內(nèi)核加上狀態(tài)處理,也是可行的。每個(gè)內(nèi)核都含有一個(gè)類(lèi)組件的集合,在使用的時(shí)候,必須對(duì)這些類(lèi)進(jìn)行實(shí)例化或從中派生出自己的對(duì)象。
幾乎任何核心類(lèi)都有一個(gè)初始化函數(shù)和一個(gè)關(guān)閉函數(shù),通常這兩個(gè)函數(shù)的名稱(chēng)分別為Init和Shutdown,但某些情況下這兩個(gè)函數(shù)的名稱(chēng)為Create 和 Free。內(nèi)核中的許多函數(shù),調(diào)用時(shí)都會(huì)返回一個(gè)BOOL值,如果函數(shù)執(zhí)行成功,就返回TRUE;反之如果函數(shù)調(diào)用失敗就返回FALSE。許多類(lèi)實(shí)例在使用之前,必須進(jìn)行初始化,并且要在使用完之后,在適當(dāng)?shù)臅r(shí)候關(guān)閉這些類(lèi)實(shí)例以釋放系統(tǒng)資源。一旦初始化之后,就可以使用類(lèi)對(duì)象了。
系統(tǒng)內(nèi)核
系統(tǒng)內(nèi)核處理初始化、通常的Windows游戲應(yīng)用程序的程序流、進(jìn)程、狀態(tài)以及數(shù)據(jù)包。創(chuàng)建新游戲工程,首先需要從此內(nèi)核入手。
使用 APPLICATION 核心對(duì)象 APPLICATION 是系統(tǒng)內(nèi)核中最有用的核心對(duì)象,它可以用于創(chuàng)建應(yīng)用程序的窗口和控制程序流。此對(duì)象能夠注冊(cè)窗口類(lèi)、創(chuàng)建應(yīng)用程序窗口以及處理應(yīng)用程序窗口消息的消息泵,并且在適當(dāng)?shù)臅r(shí)候調(diào)用內(nèi)部的類(lèi)函數(shù)。 APPLICATION通過(guò)調(diào)用三個(gè)用戶(hù)提供的(通過(guò)一個(gè)派生的類(lèi)聲明)重載函數(shù)Init,Shutdown和Frame來(lái)處理應(yīng)用程序。< br>
為了包含公共頭文件,我們需要定義一個(gè)頭文件Core_Global.h:
/**************************************************
PURPOSE:
Include common game core header file.
**************************************************/
#ifndef _CORE_GLOBAL_H_
#define _CORE_GLOBAL_H_
#define DIRECTINPUT_VERSION 0x0800
// Windows includes
#include <windows.h>
// Standard ANSI-C includes
#include <stdio.h>
// DirectX includes
#include "d3d9.h"
#include "d3dx9.h"
#include "dmusici.h"
#include "dsound.h"
#include "dplay8.h"
#include "dpaddr.h"
#include "dinput.h"
#include "dshow.h"
#include "dxfile.h"
// Core includes
#include "Core_System.h"
//#include "Core_Graphics.h"
//#include "Core_Input.h"
//#include "Core_Sound.h"
//#include "Core_Network.h"
#pragma warning(disable : 4996)
#endif
接下來(lái)是類(lèi)
APPLICATION的定義:
/*************************************************************************
PURPOSE:
Defines application main framework.
*************************************************************************/
#ifndef _CORE_SYSTEM_H_
#define _CORE_SYSTEM_H_
#include <windows.h>
//==========================================================================
// Defines application main framework.
//==========================================================================
class APPLICATION
{
private:
HINSTANCE _inst;
HWND _hwnd;
protected:
char _class_name[MAX_PATH];
char _caption[MAX_PATH];
WNDCLASSEX _win_class;
DWORD _style;
DWORD _x_pos, _y_pos;
DWORD _width, _height;
public:
APPLICATION();
HWND Get_Hwnd();
HINSTANCE Get_Inst();
BOOL Run();
void Error(BOOL is_fatal, char* text,
);
void Move(long x_pos, long y_pos);
void Resize(long width, long height);
void Show_Mouse(BOOL is_show = TRUE);
virtual LRESULT FAR PASCAL Msg_Proc(HWND hwnd, UINT msg_id, WPARAM wParam, LPARAM lParam)\
{
return DefWindowProc(hwnd, msg_id, wParam, lParam);
}
virtual BOOL Init() { return TRUE; }
virtual BOOL Shutdown() { return TRUE; }
virtual BOOL Frame() { return TRUE; }
};
static APPLICATION* g_application = NULL;
static LRESULT FAR PASCAL App_Window_Proc(HWND hwnd, UINT msg_id, WPARAM wParam, LPARAM lParam);
#endif
下面給出類(lèi)APPLICATION的實(shí)現(xiàn):
/***********************************************************************************
PURPOSE:
Capsulates application main framework.
***********************************************************************************/
#include "Core_Global.h"
//-----------------------------------------------------------------------------
// The message procedure - empty except for destroy message.
//-----------------------------------------------------------------------------
LRESULT FAR PASCAL App_Window_Proc(HWND hwnd, UINT msg_id, WPARAM wParam, LPARAM lParam)
{
switch(msg_id)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return g_application->Msg_Proc(hwnd, msg_id, wParam, lParam);
}
//-----------------------------------------------------------------------------
// Constructor, initialize window class.
//-----------------------------------------------------------------------------
APPLICATION::APPLICATION()
{
// save instance handle
g_application = this;
// Get the instance handle
//
// Retrieves a module handle for the specified module if the file has been mapped into the address space
// of the calling process.
_inst = GetModuleHandle(NULL);
// Set a default window class and caption
strcpy(_class_name, "AppClass");
strcpy(_caption, "Application Caption");
// Set default window style, position, width, height.
_style = WS_OVERLAPPEDWINDOW;
_x_pos = 0;
_y_pos = 0;
_width = 256;
_height = 256;
// Set default WNDCLASSEX structure
_win_class.cbSize = sizeof(WNDCLASSEX);
_win_class.style = CS_CLASSDC;
_win_class.lpfnWndProc = App_Window_Proc;
_win_class.cbClsExtra = 0;
_win_class.cbWndExtra = 0;
_win_class.hInstance = _inst;
_win_class.hIcon = LoadIcon(NULL, IDI_APPLICATION);
_win_class.hCursor = LoadCursor(NULL, IDC_ARROW);
_win_class.hbrBackground = NULL;
_win_class.lpszMenuName = NULL;
_win_class.lpszClassName = _class_name;
_win_class.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
}
//-----------------------------------------------------------------------------
// Get application window handle.
//-----------------------------------------------------------------------------
HWND APPLICATION::Get_Hwnd()
{
return _hwnd;
}
//-----------------------------------------------------------------------------
// Get application window instance.
//-----------------------------------------------------------------------------
HINSTANCE APPLICATION::Get_Inst()
{
return _inst;
}
//-----------------------------------------------------------------------------
// Start application.
//-----------------------------------------------------------------------------
BOOL APPLICATION::Run()
{
MSG msg;
// Register window class
if(! RegisterClassEx(&_win_class))
return FALSE;
// Create the main window
_hwnd = CreateWindow(_class_name, _caption, _style, _x_pos, _y_pos, _width, _height, NULL, NULL, _inst, NULL);
// Create window failed
if(_hwnd == NULL)
return FALSE;
// Show and update the window
ShowWindow(_hwnd, SW_NORMAL);
UpdateWindow(_hwnd);
// Make sure client area is correct size
Resize(_width, _height);
// Initialize COM
CoInitialize(NULL);
if(Init())
{
// Enter the message pump
ZeroMemory(&msg, sizeof(MSG));
while(msg.message != WM_QUIT)
{
// Handle windows messages (if any)
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
// Do per-frame processing, break on FALSE return value.
if(! Frame())
break;
}
}
}
Shutdown();
// Shutdown COM
CoUninitialize();
// Unregister the window class
UnregisterClass(_class_name, _inst);
return TRUE;
}
//-----------------------------------------------------------------------------
// Show error message box.
//-----------------------------------------------------------------------------
void APPLICATION::Error(BOOL is_fatal, char *text,
)
{
char caption_text[12];
char error_text[2048];
va_list valist;
// Build the message box caption based on fatal flag
strcpy(caption_text, is_fatal ? "Fatal Error" : "Error");
// Build variable text buffer
va_start(valist, text);
vsprintf(error_text, text, valist);
va_end(valist);
// Display the message box
MessageBox(NULL, error_text, caption_text, MB_OK | MB_ICONEXCLAMATION);
// Post a quit message if error was fatal.
if(is_fatal)
PostQuitMessage(0);
}
//-----------------------------------------------------------------------------
// Move window to new position.
//-----------------------------------------------------------------------------
void APPLICATION::Move(long x_pos, long y_pos)
{
RECT client_rect;
GetClientRect(_hwnd, &client_rect);
MoveWindow(_hwnd, x_pos, y_pos, client_rect.right, client_rect.bottom, TRUE);
}
//-----------------------------------------------------------------------------
// Resize window to new width and height.
//-----------------------------------------------------------------------------
void APPLICATION::Resize(long width, long height)
{
RECT window_rect, client_rect;
// Retrieves the dimensions of the bounding rectangle of the specified window.
// The dimensions are given in screen coordinates that are relative to the upper-left corner of the screen.
GetWindowRect(_hwnd, &window_rect);
// Retrieves the coordinates of a window's client area.
// The client coordinates specify the upper-left and lower-right corners of the client area.
// Because client coordinates are relative to the upper-left corner of a window's client area,
// the coordinates of the upper-left corner are (0,0).
GetClientRect(_hwnd, &client_rect);
long new_window_width = (window_rect.right - window_rect.left) - client_rect.right + width;
long new_window_height = (window_rect.bottom - window_rect.top) - client_rect.bottom + height;
// Changes the position and dimensions of the specified window.
// For a top-level window, the position and dimensions are relative to the upper-left corner of the screen.
// For a child window, they are relative to the upper-left corner of the parent window's client area.
MoveWindow(_hwnd, window_rect.left, window_rect.top, new_window_width, new_window_height, TRUE);
}
//-----------------------------------------------------------------------------
// Show or hide mouse.
//-----------------------------------------------------------------------------
void APPLICATION::Show_Mouse(BOOL is_show)
{
ShowCursor(is_show);
}
APPLICATION類(lèi)被設(shè)計(jì)成自運(yùn)行模式,用戶(hù)只需將它植入自己的游戲代碼中即可。要使用 APPLICATION,首先必須將APPLICATION類(lèi)作為基類(lèi)創(chuàng)建一個(gè)派生類(lèi)。這樣做就可以重載特定的函數(shù)以滿(mǎn)足特定的要求。要重載的這些函數(shù),就是前面提到的Init、Shutdown、和Frame。
在A(yíng)PPLICATION::Init函數(shù)中,要書(shū)寫(xiě)類(lèi)的所有初始化代碼,諸如加載數(shù)據(jù)、準(zhǔn)備處理狀態(tài)等。和Init函數(shù)相對(duì)應(yīng)的函數(shù)是 APPLICATION::Shutdown,此函數(shù)釋放所有之前分配的資源。Shutdown函數(shù)是最后一個(gè)調(diào)用的函數(shù),而 Init是第一個(gè)調(diào)用的函數(shù)。
每次遍歷消息泵(在消息泵中不處理Windows消息)時(shí),都要調(diào)用APPLICATION::Frame函數(shù)。Frame函數(shù)處理游戲的一幀,包括處理用戶(hù)輸入、檢測(cè)網(wǎng)絡(luò)數(shù)據(jù)以及繪制圖形等。 一般都不重載消息處理函數(shù),除非要自己書(shū)寫(xiě)代碼處理Windows消息,要處理消息,需要重載
APPLICATION::Msg_Proc。
下面給出測(cè)試代碼來(lái)測(cè)試類(lèi)APPLICATION:
/*****************************************************************************
PURPOSE:
Test for class APPLICATION.
*****************************************************************************/
#include "Core_System.h"
#pragma warning(disable : 4996)
//===========================================================================
// Defines class APP which public inherits from class APPLICATION.
//===========================================================================
class APP : public APPLICATION
{
private:
char* _name;
public:
APP();
BOOL Init();
BOOL Shutdown();
BOOL Frame();
};
//-----------------------------------------------------------------------------
// Consturctor, initialize member data.
//-----------------------------------------------------------------------------
APP::APP()
{
_name = NULL;
// set window style, position, width and height.
_style = WS_OVERLAPPEDWINDOW;
_x_pos = 100;
_y_pos = 20;
_width = 400;
_height = 400;
// set class name and caption
strcpy(_class_name, "NameClass");
strcpy(_caption, "My NAme Example");
}
//-----------------------------------------------------------------------------
// Initialize application.
//-----------------------------------------------------------------------------
BOOL APP::Init()
{
if((_name = new char[9]))
{
strcpy(_name, "lovedday");
return TRUE;
}
return FALSE;
}
//-----------------------------------------------------------------------------
// Shutdown application.
//-----------------------------------------------------------------------------
BOOL APP::Shutdown()
{
delete[] _name;
_name = NULL;
return TRUE;
}
//-----------------------------------------------------------------------------
// Do a frame.
//-----------------------------------------------------------------------------
BOOL APP::Frame()
{
// If user clicks button CANCEL, then exit application.
if(MessageBox(Get_Hwnd(), _name, "My name is", MB_OKCANCEL) == IDCANCEL)
return FALSE;
return TRUE;
}
int PASCAL WinMain(HINSTANCE inst, HINSTANCE, LPSTR cmd_line, int cmd_show)
{
APP app;
return app.Run();
}
點(diǎn)擊下載源碼和工程 運(yùn)行截圖:
閱讀下篇:
創(chuàng)建游戲內(nèi)核(2)