天行健 君子当自强而不息

创建游戏内核(1)

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


posted on 2007-08-14 22:14 lovedday 阅读(1300) 评论(5)  编辑 收藏 引用

评论

# re: 创建游戏内核(1) 2007-08-21 11:49 boilerroom

用的是什么ide阿,看着好酷阿  回复  更多评论   

# re: 创建游戏内核(1) 2007-08-21 14:18 lovedday

?
VISTA + VS 2005.  回复  更多评论   

# re: 创建游戏内核(1) 2008-03-26 16:41 李锦俊

真是牛人啊!代码里一堆一堆的英文注释。看得我晕乎乎  回复  更多评论   

# re: 创建游戏内核(1) 2008-03-26 16:55 lovedday

啥牛人啊,大菜鸟一个,这些文章不过是自己看书时的摘要,写出来也是方便自己查阅的,没想到对大家还有用。  回复  更多评论   

# re: 创建游戏内核(1) 2010-03-23 19:46 bruce

请问这个用的directx什么版本?我编译一直出问题。  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论