zoukankan      html  css  js  c++  java
  • 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);
    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.0f10000.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.0f0);
        
    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基本框架就完成了~

    如下执行效果图:

  • 相关阅读:
    jQuery动态效果学习笔记
    jQuery HTML操作学习笔记
    学习编程有必要做笔记吗?
    2017阿里巴巴面试技术挑战赛试题泄露
    来自IOS开发工程师的零基础自学HTML5经验分享
    前端学习笔记之HTML DOM操作
    web前端攻城狮整理的收藏夹
    WEB前端开发初学者必看的学习路线(附思维导图)
    搜狗员工用百度算什么,谷歌员工当着老板的面用bing,结果悲剧了!
    常用站长工具软件汇总,有没有一款你在用?
  • 原文地址:https://www.cnblogs.com/kenkao/p/2086411.html
Copyright © 2011-2022 走看看