写程序时,处理重复(或寻常)代码的技巧就是创建一个有用的函数构成的核心库,由这个核心库来处理重复的代码。使用这个函数库,就无需反复书写相同的 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)