寫程序時,處理重復(或尋常)代碼的技巧就是創建一個有用的函數構成的核心庫,由這個核心庫來處理重復的代碼。使用這個函數庫,就無需反復書寫相同的 DirectX或Windows代碼,因而能夠幫助程序員快速創建游戲工程。
理解內核概念 游戲內核(game core)是用來簡化DirectX和Windows編程的一些庫的集合。游戲內核中幾乎含有一個游戲工程需要使用的所有函數,這些函數一般用于繪制圖形、播放聲音、處理用戶輸入以及應用程序。也就是說,使用游戲內核,就無需在每次開始一個新的游戲工程時,再去處理底層的DirectX或Windows 代碼。
游戲內核主要包括5個內核:
系統內核(system core) :用于Window處理過程,包括注冊窗口類,創建應用程序窗口以及處理進程、狀態和數據包。
圖形內核(graphics core):用于繪制圖形,使用2D方法快速繪制圖像或使用諸如動畫網格之類的3D方法渲染場景。
輸入內核(input core): 處理來自鍵盤、鼠標和游戲桿的用戶輸入。
聲音內核(sound core): 讓用戶享受多聲道的聲音和音樂,此內核能夠改變樂器聲音并帶來獨特的音樂感受。
網絡內核(network core): 此內核用于將客戶機連接到Internet以及一些大型在線游戲的服務器,使用此內核,就能夠暢游網絡。
每個內核都可以單獨使用,并且在一個游戲工程中沒有必要包含所有的內核,如果只想擁有聲音特性,只包含聲音內核即可,如果想給系統內核加上狀態處理,也是可行的。每個內核都含有一個類組件的集合,在使用的時候,必須對這些類進行實例化或從中派生出自己的對象。
幾乎任何核心類都有一個初始化函數和一個關閉函數,通常這兩個函數的名稱分別為Init和Shutdown,但某些情況下這兩個函數的名稱為Create 和 Free。內核中的許多函數,調用時都會返回一個BOOL值,如果函數執行成功,就返回TRUE;反之如果函數調用失敗就返回FALSE。許多類實例在使用之前,必須進行初始化,并且要在使用完之后,在適當的時候關閉這些類實例以釋放系統資源。一旦初始化之后,就可以使用類對象了。
系統內核
系統內核處理初始化、通常的Windows游戲應用程序的程序流、進程、狀態以及數據包。創建新游戲工程,首先需要從此內核入手。
使用 APPLICATION 核心對象 APPLICATION 是系統內核中最有用的核心對象,它可以用于創建應用程序的窗口和控制程序流。此對象能夠注冊窗口類、創建應用程序窗口以及處理應用程序窗口消息的消息泵,并且在適當的時候調用內部的類函數。 APPLICATION通過調用三個用戶提供的(通過一個派生的類聲明)重載函數Init,Shutdown和Frame來處理應用程序。< br>
為了包含公共頭文件,我們需要定義一個頭文件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
接下來是類
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
下面給出類APPLICATION的實現:
/***********************************************************************************
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類被設計成自運行模式,用戶只需將它植入自己的游戲代碼中即可。要使用 APPLICATION,首先必須將APPLICATION類作為基類創建一個派生類。這樣做就可以重載特定的函數以滿足特定的要求。要重載的這些函數,就是前面提到的Init、Shutdown、和Frame。
在APPLICATION::Init函數中,要書寫類的所有初始化代碼,諸如加載數據、準備處理狀態等。和Init函數相對應的函數是 APPLICATION::Shutdown,此函數釋放所有之前分配的資源。Shutdown函數是最后一個調用的函數,而 Init是第一個調用的函數。
每次遍歷消息泵(在消息泵中不處理Windows消息)時,都要調用APPLICATION::Frame函數。Frame函數處理游戲的一幀,包括處理用戶輸入、檢測網絡數據以及繪制圖形等。 一般都不重載消息處理函數,除非要自己書寫代碼處理Windows消息,要處理消息,需要重載
APPLICATION::Msg_Proc。
下面給出測試代碼來測試類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();
}
點擊下載源碼和工程 運行截圖:
閱讀下篇:
創建游戲內核(2)