为了更好的了解DX和游戏的架构,同时也为了练习Code Reading的能力(好官僚的口气),我打算把DX SDK中的Demo——donuts4(面包圈大战)仔细的读一遍。以前也没读过什么长的东西,还一直以为写东西才是根本。现在看来,读东西同样的重要。
文件构成
除了resource.h外,donuts4共有18个cpp/h文件。根据头文件里面的注释,首先对这些文件有一个大概的了解:
donuts.cpp/.h 是donuts4的主文件了。里面说,该游戏使用了Direct3D,DirectMusic,DirectSound和DirectInput。
StdAfx.cpp/.h 预编译头文件。
GameMenu.cpp/.h 用于显示游戏的菜单。(我怎么没看见游戏里面有菜单?!)
Profile.cpp/.h 用于读写INI文件。
FileWatch.cpp/.h 用于查看一些文件是否被修改了。(无法理解这个的作用……)
NotifyTool.cpp/.h 似乎是一个IDirectMusicTool的扩展。
3DModel.cpp/.h 一个类C3DModel的实现。应该是对3D模型的抽象吧。
DisplayObject.cpp/.h 类CDisplayObject的实现。
3DDisplayObject.cpp/.h 一个继承于CDisplayObject的类。不知道和C3DModel有什么联系。
3DDrawManager.cpp/.h 这个应该是用来画东东的Manager了。
InputManager.cpp/.h 管理Input的类。
HeightMap.cpp/.h 类CHeightMap的实现,应该是和Terrain相关的。
TerrainMesh.cpp/.h 似乎是Terrain里面的Mesh的抽象。
TerrainEngine.cpp/.h 地形引擎。
ParticleSystem.cpp/.h 粒子系统(也没看见哪用了……)。
Bullet.cpp/.h 子弹嘛,就是面包圈了……
EnemyShip.cpp/.h 敌人,就是那个紫色的东西。
PlayerShip.cpp/.h 自己的船,不过似乎不可见的样子(因为是第一视角)。
donuts.h中的类
donuts.h中只声明了两个类。一个是非常短小的C3DAudioPath类,一个是非常巨大的CMyApplication类。CMyApplication类光声明就用了160行……
我本以为donuts使用的应该是DX的Common Framework呢,但是似乎不是。CMyApplication并不是继承于CApplication的。但是类里面的结构和CApplication差不多,应该Common Framework的一个简化版或者是改进版。
donuts只使用了Common中的D3DFile,D3DFont以及一些Util。
另外,donuts.h还对一些Custom Vertex和相应的FVF做了声明。
程序的入口
donuts.cpp有96K这么大,2000多行,看着就头痛。
先找程序的入口WinMain吧。WinMain写在了很显眼的前面。除了设定随机种子以外,就是实例化CMyApplication了。可以看出app的使用分两步,第一步是Create(),第二步是Run()。这和D3DApp似乎也差不多,其实这种方法挺好的。
CMyApplication的构造函数就是对其所有需要init的内部成员都init一下。满眼的匈牙利阿……
Create()和Run()都不长,可以仔细看看。
Create()照例是声明一个窗口,然后把相应的属性存了下来。值得注意的是,在Create()中调用了OneTimeSceneInit(),就是说,游戏对象的初始化也是在Create()中完成的。
Run()中也是在意料之中的封装了一个Message Pump。有个有趣的地方,在取消息的时候,donuts采用的方法是:
if(active)
msg = PeekMessage();
else
msg = GetMessage();
旁边还写上了简单的说明:如果程序是active的话,就用Peek,这样可以用idle的时间来做render。如果不是的话,就用Get,这样可是避免eat掉CPU的时间。又学了一招。
如果没有msg在wait的话,就可以render了。render的代码很怪异:
if( m_bDisplayReady )
{
if( m_pFileWatch->HaveFilesChanged( TRUE ) )
{
g_Profile.GetProfile( m_strProfilePath );
}
else
{
UpdateScene();
RenderScene();
}
}
也就是说,如果文件发生改变了的话,就要重新更新文件。它这里所说的文件应该就是ini文件了。不过…… 谁在玩游戏的时候会切出去更改配置文件?又或者游戏本身更改了配置文件(没见到Option阿)?迷惑ing……
不过可以看出,渲染分为两步UpdateScene()和RenderScene()。
另外,Create()中声明Window的时候,给定的MsgProc是StaticMsgProc()这个函数。而这个函数也仅仅是起到了一个转向的作用,把消息交给了app.MsgProc()来处理了。