65.9K
CodeProject 正在变化。 阅读更多。
Home

一个简单而强大的游戏引擎

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.45/5 (16投票s)

2007 年 7 月 23 日

4分钟阅读

viewsIcon

93413

downloadIcon

9179

本文讨论了一个简单而强大的游戏引擎, 用于简化游戏编程。

Screenshot - image001.jpg

这张图片是从 3ds Max 截取的。这款 F-16 有 58,133 个顶点,构成 174,399 个三角形。

引言

本文介绍了一个使用 DirectX 的简单而强大的游戏引擎——3D、输入、音乐和显示。有了这个引擎,您可以渲染复杂的三维模型,如地形、城市、人物、汽车等,还可以播放音乐,并为音乐添加三维效果,如速度、位置和方向。您可以播放电影——任何可以使用 Windows Media Player 播放的电影——还可以使用直接输入以全速和高级用法获取键盘、鼠标和操纵杆的输入。本文包含一个 3ds Max 的插件,即游戏建模第一名,它可以将 3ds Max 场景导出到 SZM 格式。SZM 模型格式是一种非常简单但功能强大的建模格式。 这里是 SZM 模型格式的文档。

使用代码

每个 3D 游戏都有一个 Init_Game()Game_Loop()Game_Clean_Up() 函数。在 Init_Game() 中,您初始化 DirectX 并加载所需的模型、音乐和视频。在游戏循环中,您检查是否有任何来自键盘或鼠标的输入并处理该输入。您还可以计算每个对象应该在何处。完成这些之后,您将其渲染到屏幕上。在游戏清理阶段,您执行退出游戏时所需的任何清理工作。

初始化游戏

首先,创建一个窗口。然后,在进入消息循环之前,按如下方式开始初始化:

//------------------------------------------------
::ShowWindow(g_GlobalData.hWnd,SW_SHOW);
::UpdateWindow(g_GlobalData.hWnd);
//------------------------------------------------

//
//Intialize Program.
//

if(!::Init_Game())
{
    DestroyWindow(g_GlobalData.hWnd);
    Return false;
}

//------------------------------------------------

下面是 Init_Game() 例程,它执行以下操作:

  1. 初始化一些全局变量,例如缩放和视点位置,显示边界框等。
  2. 初始化一些 DirectX3D 的变量,例如启用 Z 缓冲区、全局环境光、3D 光照等。
  3. 调用 InitDirectX3D()
  4. 创建一个要显示的物体。
  5. 播放一些视频,仅当您现在想看视频时。
  6. 视频播放完成后,如果您决定播放视频。
  7. 您可以播放一些音乐。
  8. 初始化直接输入,即鼠标和键盘输入。
  9. 初始化一个 CD3DFont 类。
  10. 初始化完成!
////////////////////////////////////////////////////
//
////////////////////////////////////////////////////
BOOL Init_Game()
{
    //Move Eye position back, so you can see the scene.
    g_GlobalData.CameraEyeZ = -15000;
    g_GlobalData.EyeX_Rotation=0;
    g_GlobalData.EyeY_Rotation=0;
    g_GlobalData.EyeZ_Rotation=0;
    
    // How much to zoom (when using the mouse wheel).
    g_GlobalData.m_dwMouseWheel = 10;
    g_GlobalData.m_dwCameraMoveY = 10;
    g_GlobalData.m_dwCameraMoveZ = 10;
    g_GlobalData.bShowBoundingBoxs = TRUE;
    g_GlobalData.bShowObjectBoundingBoxs = TRUE;
    g_GlobalData.bShowWireFrame = FALSE;
    g_GlobalData.dwInputSystemOldTime = 0;
    g_GlobalData.bProcessInput = TRUE;

    //---------------------------------
    // Init the values that would be past to SetRenderState
    g_DirectX3D.m_bEnableZBuffer = TRUE;
    g_DirectX3D.m_dwGlobalAmbient = D3DCOLOR_ARGB(255,0,0,0);
    g_DirectX3D.m_bEnableDirect3DLighting = TRUE;
    g_DirectX3D.m_bEnableBackFacing = FALSE;
    
    //---------------------------------
    //Now Init DirectX 3d.
    if(!g_DirectX3D.InitDirectX3D(g_GlobalData.hWnd))
    return FALSE;

    //---------------------------------
    // g_DirectX3D.WritePC_CapabilitiesToFile("PC_Capabilities.txt");
    //---------------------------------
    
    //Create or Load an object to display for an example.
    if(!::CreateAnObjectToDisplay())
    return FALSE;
    
    //---------------------------------
    //g_DirectX3D.ShowFog();
    //---------------------------------
    
    //Clear Display
    g_DirectX3D.Clear_Display(TRUE,TRUE,FALSE,DISPLAY_COLOR);
    g_DirectX3D.Flip();
    //---------------------------------
    
    //->Show a Video.
    //---------------------------------
    g_DirectShow.Play(L"Movies\\Nsync - Bye Bye Bye.mpg",g_GlobalData.hWnd);
    
    //->Turn on some Music.
    //---------------------------------
    if(!g_DirectSound.Init())
    return FALSE;
    
    if(!g_DirectSound.Load(L"Music\\Britney Spears – Crazy.mp3"))
    return FALSE;
    
    if(!g_DirectSound.CreateAudioPath(FALSE))
    return FALSE;

    if(!g_DirectSound.SetVolume(-1600))
    return FALSE;
    
    if(!g_DirectSound.PlayAudioPath(0))
    return FALSE;

    //---------------------------------
    g_GlobalData.bInitWasDone = TRUE;
    //---------------------------------
    if(!g_Keyboard.Init())return FALSE;
    if(!g_Mouse.Init())return FALSE;
    //---------------------------------

    //----------------------------
    g_pFont = new CD3DFont( _T("Times New Roman"), 14, D3DFONT_BOLD );
    
    g_pFont->InitDeviceObjects( g_DirectX3D.m_pD3dDevice );
    g_pFont->RestoreDeviceObjects();
    //----------------------------

    return TRUE;
}

游戏循环

在消息循环中,按如下方式调用 Prog_Loop()

//------------------------------------------------
while(1)
{
    if( ::PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) )
    {
        if( !::GetMessage( &msg, NULL, 0, 0 ) )
        return msg.wParam;

        ::TranslateMessage(&msg); 
        ::DispatchMessage(&msg);
    }
    else if(g_GlobalData.bInitWasDone)
    {
        ::Game_Loop();
    }
    else
    {
        // make sure we go to sleep if we have nothing else to do
        ::WaitMessage();
    }
}
//------------------------------------------------
这是 Game_Loop() 的作用:
  1. 它检查是否是时候从键盘和鼠标获取输入了。请注意,您可以对其进行更改,使其在每次进入 Game_Loop() 时检查输入。
  2. 设置变换。
  3. 清除显示。
  4. 渲染一个物体。
  5. 绘制文本。
  6. 翻转后缓冲区到前缓冲区,显示新渲染的场景。
  7. 渲染完成,直到下一个循环。
////////////////////////////////////////////////////
//
////////////////////////////////////////////////////
BOOL Game_Loop()
{
    //Get time.
    g_GlobalData.dwCurrentTime = ::timeGetTime();
    //--------------------------------------
    //If passed a certain time check if there is any input.
    if(g_GlobalData.dwCurrentTime > (g_GlobalData.dwInputSystemOldTime+10))
    {
        if(g_GlobalData.bProcessInput)
        {
            g_Keyboard.ProcessKey();
            g_Mouse.ProcessMouse();
        }
    
        g_GlobalData.dwInputSystemOldTime = g_GlobalData.dwCurrentTime;
    }
    
    //--------------------------------------
    //Set Transformations.
    if(!g_DirectX3D.SetTransformations())
    return FALSE;

    //Clear Display.
    g_DirectX3D.Clear_Display(TRUE,TRUE,FALSE,DISPLAY_COLOR);

    //Render Object.
    g_DirectX3D.RenderObject(&g_GlobalData.Object);
    
    //--------------------------------------
    //Draw Text.
    g_pFont->DrawText( 380, 0, D3DCOLOR_ARGB(255,255,0,0), "My-Game!" );
    //--------------------------------------

    //Flip backbuffer to front buffer.
    g_DirectX3D.Flip();

    return TRUE;
}

结果

您将获得以下效果:

Screenshot - image003.jpg

附加功能

  • 显示文本
  • 光照
  • 平铺纹理
  • 将 PC 功能写入文件
  • 窗口模式或全屏

如何编译

您必须拥有 DirectX 8 或 9 SDK。以下是如何包含 DX SDK 的 "include" 和 "lib" 目录的路径:

Visual Studio 6

工具 -> 选项 -> 目录 (选项卡) -> (包含文件) / (库文件) 从组合框中 -> 添加路径

Visual Studio 8 (VS 2005)

工具 -> 选项 -> 项目和解决方案 (从树状视图) -> Visual C++ 目录 -> (包含文件) / (库文件) -> 添加路径

最好将新添加的路径放在列表的最后。

SZM 3D 模型格式

在文件开头,有一个 SZM_File_header 结构。紧随其后的是一个 DWORD,它保存 MyMesh 结构的数量。这就是文件头:

////////////////////////////////////////////////////
// Header
////////////////////////////////////////////////////

typedef struct _SZM_File_header
{
    DWORD SZM_Sig;//'SZM '
    DWORD version1;//'0004'
    DWORD version2;//'0001'
} 

SZM_File_header;

这是保存文件中网格数量的 DWORD

////////////////////////////////////////////////////
//
////////////////////////////////////////////////////

DWORD nMeshesInFile; // Number of meshes in the file.

这是 MyMesh 的声明方式:

////////////////////////////////////////////////////
// Now comes nMeshesInFile of MyMesh structures.
////////////////////////////////////////////////////

typedef struct _MyMesh
{
    //--------------------------
    DWORD nMeshName; // strlen(szMeshName)+1
    char *szMeshName; // Mesh name.
    DWORD nTextureFileName; // strlen(szTextureFileName)+1
    char *szTextureFileName;// Texture File Name.
    float UTilingData; // Texture n tiling at the U axis.
    float VTilingData; // Texture n tiling at the V axis. 
    float UOffsetData; // Texture Offset at the U axis.
    float VOffsetData; // Texture Offset at the V axis.
    //--------------------------

    D3DXMATRIX matTransformation;
    //The transformation matrix that was applied to the mesh.
    DWORD nVertex; // Number of Vertex of the mesh.
    MyD3DVERTEX *pVertex; // Vertex Buffer.
    //--------------------------

    MyMaterial Material; // Mesh Material.

    //--------------------------
    // Is there a Bounding Box.
    // The reason that some meshes don't have bounding boxes 
    // is that some meshes are part of a mesh that came before 
    // them, so that mesh bounding box also bounds on the following 
    // mesh (The reason that some mashed are divided to parts are 
    // because it can be meshes that different parts have a different 
    // material, so the mesh was divided to parts).

    BOOL bHasBoundingBox; 
    Box BoundingBox; // Bounding Box.
    Box3 MaxMinBox; // Mesh Max and Min points.
    //--------------------------

    DWORD nMesh; // Mesh-ID.
    DWORD dwLevelInTree; // Level in the tree.
    //--------------------------
} 
MyMesh;

////////////////////////////////////////////////////
//
////////////////////////////////////////////////////

有关更多信息,请参见 _SZM_Format.h_ 和 _SZM_Loader.cpp_。

如何制作 3D 模型

排名第一的游戏建模软件是 3ds Max,但 3ds Max 不是免费的。如果您想要一款免费的 3D 建模软件,可以使用 Blender 3D。我并不太喜欢使用 Blender 3D,但也许您可以习惯它。有关更多信息,您可以阅读 这里

资源和参考

  • 有关游戏引擎的更多信息,请参阅 此处
  • 有关物理引擎的更多信息,请参阅 此处
  • 有关游戏开发的更多信息,请参阅 此处

评论、建议或修正

我很想听听关于本文的任何评论、建议或修正。这将激励我写更多文章。祝您游戏愉快!

历史

  • 2007 年 7 月 23 日 - 首次发布
© . All rights reserved.