Direct3D轮回:快速构建基于win32工程的Direct3D游戏框架

前段时间一直混迹于C++与C#语言,徘徊于DirectX与Xna之间,一直没什么大的收获。

重新拾回C++&DirectX,有一种返璞归真的感慨~ 多少有一些心得,简单总结一点~ 请园子的前辈们多多指教,多多拍砖^ ^

 

用过Xna的朋友都知道,客户在Xna中从来不用自己去实现3D设备的初始化,游戏的主循环,甚至是退出时的设备资源释放等等相关事宜。

不过很不幸,DirectX没有赐予大家这种特权,相关工作我们需要自己实现:

1.新建一个Win32工程,并在此基础上新增CD3DInit类(这里其实没有必要真正形成类,个人感觉全局函数用起来反倒方便);

/* ----------------------------------
代码清单:D3DInit.h
来自:
http://kenkao.cnblogs.com/
----------------------------------
*/

#pragma  once

#include 
< windows.h >
#include 
" d3d9.h "
#include 
" d3dx9.h "

#pragma  comment(lib, "d3d9.lib")
#pragma  comment(lib, "d3dx9.lib")

#define  ReleaseCOM(x){if(x!=NULL) x->Release();x=NULL;}

HRESULT InitD3D(IDirect3D9 
** ppD3D, 
                IDirect3DDevice9 
** ppD3DDevice,
                HWND hWnd, BOOL ForceWindowed 
=  FALSE,
                BOOL MultiThreaded 
=  FALSE);
Direct3D轮回:快速构建基于win32工程的Direct3D游戏框架 D3DInit.cpp
/* ----------------------------------
代码清单:D3DInit.cpp
来自:
http://kenkao.cnblogs.com/
----------------------------------
*/

#include 
" D3DInit.h "

/* ---------------------------------------------------
参考了《Advanced Animation with DirectX》书中的实现方法

参数一:IDirect3D9指针引用(指针的指针)
参数二:IDirect3DDevice9设备指针引用
参数三:有效的窗口句柄
参数四:是否全屏
参数五:是否多线程
----------------------------------------------------
*/
HRESULT InitD3D(IDirect3D9 
** ppD3D, 
                IDirect3DDevice9 
** ppD3DDevice,
                HWND hWnd, 
                BOOL ForceWindowed,
                BOOL MultiThreaded)
{
    IDirect3D9       
* pD3D        =  NULL;
    IDirect3DDevice9 
* pD3DDevice  =  NULL;
    HRESULT           hr;

    
//  错误检查
     if ( ! ppD3D  ||   ! ppD3DDevice  ||   ! hWnd)
        
return  E_FAIL;

    
//  初始化 Direct3D 
     if ((pD3D  =  Direct3DCreate9(D3D_SDK_VERSION))  ==  NULL)
        
return  E_FAIL;
    
* ppD3D  =  pD3D;

    
//  是否使用全屏状态
     int  Mode;
    
if (ForceWindowed  ==  TRUE)
        Mode 
=  IDNO;
    
else
        Mode 
=  MessageBox(hWnd,  " Use fullscreen mode? (640x480x16) " " Initialize D3D " , MB_YESNO  |  MB_ICONQUESTION);

    
//  全屏与非全屏状态下的参数设置
    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory(
& d3dpp,  sizeof (d3dpp));

    
if (Mode  ==  IDYES) {

        DWORD     Width  
=   640 ;
        DWORD     Height 
=   480 ;
        D3DFORMAT Format 
=  D3DFMT_R5G6B5;

        d3dpp.BackBufferWidth  
=  Width;
        d3dpp.BackBufferHeight 
=  Height;
        d3dpp.BackBufferFormat 
=  Format;
        d3dpp.SwapEffect       
=  D3DSWAPEFFECT_FLIP;
        d3dpp.Windowed         
=  FALSE;
        d3dpp.EnableAutoDepthStencil 
=  TRUE;
        d3dpp.AutoDepthStencilFormat 
=  D3DFMT_D16;
        d3dpp.FullScreen_RefreshRateInHz 
=  D3DPRESENT_RATE_DEFAULT;
        d3dpp.PresentationInterval       
=  D3DPRESENT_INTERVAL_DEFAULT;
    } 
else  {

        RECT ClientRect, WndRect;
        GetClientRect(hWnd, 
& ClientRect);
        GetWindowRect(hWnd, 
& WndRect);

        DWORD DesiredWidth  
=   640 ;
        DWORD DesiredHeight 
=   480 ;
        DWORD Width  
=  (WndRect.right  -  WndRect.left)  +  (DesiredWidth   -  ClientRect.right);
        DWORD Height 
=  (WndRect.bottom  -  WndRect.top)  +  (DesiredHeight  -  ClientRect.bottom);

        MoveWindow(hWnd, WndRect.left, WndRect.top, Width, Height, TRUE);

        D3DDISPLAYMODE d3ddm;
        pD3D
-> GetAdapterDisplayMode(D3DADAPTER_DEFAULT,  & d3ddm);

        d3dpp.BackBufferWidth  
=  DesiredWidth;
        d3dpp.BackBufferHeight 
=  DesiredHeight;
        d3dpp.BackBufferFormat 
=  d3ddm.Format;
        d3dpp.SwapEffect       
=  D3DSWAPEFFECT_DISCARD;
        d3dpp.Windowed         
=  TRUE;
        d3dpp.EnableAutoDepthStencil 
=  TRUE;
        d3dpp.AutoDepthStencilFormat 
=  D3DFMT_D16;
        d3dpp.FullScreen_RefreshRateInHz 
=  D3DPRESENT_RATE_DEFAULT;
        d3dpp.PresentationInterval       
=  D3DPRESENT_INTERVAL_DEFAULT;
    }

    
//  创建3D设备
    DWORD Flags =  D3DCREATE_MIXED_VERTEXPROCESSING;
    
if (MultiThreaded  ==  TRUE)
        Flags 
|=  D3DCREATE_MULTITHREADED;
    
if (FAILED(hr  =  pD3D -> CreateDevice(
        D3DADAPTER_DEFAULT, 
        D3DDEVTYPE_HAL, hWnd, Flags, 
        
& d3dpp,  & pD3DDevice)))
        
return  hr;

    
* ppD3DDevice  =  pD3DDevice;

    
//  设置投影矩阵
     float  Aspect  =  ( float )d3dpp.BackBufferWidth  /  ( float )d3dpp.BackBufferHeight;
    D3DXMATRIX matProjection;
    D3DXMatrixPerspectiveFovLH(
& matProjection, D3DX_PI / 4.0f , Aspect,  1.0f 10000.0f );
    pD3DDevice
-> SetTransform(D3DTS_PROJECTION,  & matProjection);

    
//  设置默认渲染状态
    pD3DDevice -> SetRenderState(D3DRS_LIGHTING,         FALSE);
    pD3DDevice
-> SetRenderState(D3DRS_ZENABLE,          D3DZB_TRUE);
    pD3DDevice
-> SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
    pD3DDevice
-> SetRenderState(D3DRS_ALPHATESTENABLE,  FALSE);

    
//  设置默认纹理融合态
    pD3DDevice -> SetTextureStageState( 0 , D3DTSS_COLORARG1, D3DTA_TEXTURE);
    pD3DDevice
-> SetTextureStageState( 0 , D3DTSS_COLORARG2, D3DTA_DIFFUSE);
    pD3DDevice
-> SetTextureStageState( 0 , D3DTSS_COLOROP,   D3DTOP_MODULATE);

    
//  设置默认纹理采样器
    pD3DDevice -> SetSamplerState( 0 , D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
    pD3DDevice
-> SetSamplerState( 0 , D3DSAMP_MINFILTER, D3DTEXF_LINEAR);

    
return  S_OK;
}

D3DInit.h相当于一个总的头文件,我们可以把用于初始化D3D所需要引入的头文件、库文件统统放到这个下面,供其他文件引用。

D3DInit.cpp是其实现部分,目前只有一个用于快速初始化D3D设备的InitD3D函数,代码中给出了参考书名,不得不佩服与作者的匠心独运。函数内部给出了中文注释,其奥妙留待读者自己体会~

至于ReleaseCOM宏,可以作为通用的COM安全释放函数,熟练运用D3D的朋友大都会留出这样一个宏,建议大家养成这样一个习惯~

2.新增CD3DGame类

/* ----------------------------------
代码清单:D3DGame.h
来自:
http://kenkao.cnblogs.com/
----------------------------------
*/

#pragma  once

#include 
" D3DInit.h "

//  初始化
void  Initialize(HWND hWnd);

//  内容加载
void  LoadContent();

//  逻辑更新
void  Update();

//  绘图更新
void  Draw();

//  卸载内容
void  UnloadContent();

//  释放并退出
void  Dispose();

/* ---------------------------------------------------------

1.用如下这一段替代_tWinMain消息循环的那一段

LoadContent();
ZeroMemory(&msg, sizeof(MSG));
while(msg.message != WM_QUIT) {
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Update();
Draw();
}
UnloadContent();
Dispose();

2.InitInstance的ShowWindow(hWnd, nCmdShow)之前加入

Initialize(hWnd);

----------------------------------------------------------
*/

 

/* ----------------------------------
代码清单:D3DGame.cpp
来自:
http://kenkao.cnblogs.com/
----------------------------------
*/

#include 
" StdAfx.h "
#include 
" D3DGame.h "

IDirect3D9       
* g_pD3D        =  NULL;
IDirect3DDevice9 
* g_pD3DDevice  =  NULL;

void  Initialize(HWND hWnd)
{
    InitD3D(
& g_pD3D,  & g_pD3DDevice, hWnd);
}

void  LoadContent()
{
    
}

void  Update()
{
    
}

void  Draw()
{
    g_pD3DDevice
-> Clear( 0 , NULL, D3DCLEAR_TARGET  |  D3DCLEAR_ZBUFFER, D3DCOLOR_RGBA( 100 , 149 , 237 , 255 ),  1.0f 0 );
    
if (SUCCEEDED(g_pD3DDevice -> BeginScene())) 
    {
        g_pD3DDevice
-> EndScene();
    }
    g_pD3DDevice
-> Present(NULL, NULL, NULL, NULL);
}

void  UnloadContent()
{

}

void  Dispose()
{
    ReleaseCOM(g_pD3DDevice);
    ReleaseCOM(g_pD3D);
}

安插这两个文件的目的,主要是为了构建我们自己的D3D游戏框架,各个函数的用途均已说明,完全仿Xna结构而来~(用习惯了^ ^)

我们可以按照D3DGame.h最下方的注释,将这个框架跟Win32默认代码框架关联起来。

以后所有的编码工作均在我们自己的这部分代码中实现,基本不需要再关心令人眼花缭乱的Win32工程代码。

3.改动后的Win32代码为:

int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
 UNREFERENCED_PARAMETER(hPrevInstance);
 UNREFERENCED_PARAMETER(lpCmdLine);

  // TODO: 在此放置代码。
 MSG msg;
 HACCEL hAccelTable;

 // 初始化全局字符串
 LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
 LoadString(hInstance, IDC_KEN3DGAME, szWindowClass, MAX_LOADSTRING);
 MyRegisterClass(hInstance);

 // 执行应用程序初始化:
 if (!InitInstance (hInstance, nCmdShow))
 {
  return FALSE;
 }

 hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_KEN3DGAME));


 LoadContent();
 ZeroMemory(&msg, sizeof(MSG));
 while(msg.message != WM_QUIT) {
  if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
   TranslateMessage(&msg);
   DispatchMessage(&msg);
  }
  Update();
  Draw();
 }
 UnloadContent();
 Dispose();

 return (int) msg.wParam;
}

 

 

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // 将实例句柄存储在全局变量中

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }

   Initialize(hWnd);

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

这样,一个可以无限复用的D3D基本框架就完成了~

如下执行效果图:

Direct3D轮回:快速构建基于win32工程的Direct3D游戏框架

 

你可能感兴趣的:(Win32)