我的游戏:走迷宫(DirectX版)

最近刻苦钻研两三天,终于把我的迷宫程序显示在图形化的界面上了,为此我花了大量的时间来看各类的参考书、网上搜索的资料和MSDN。我想把我的程序给大家看看,也希望大家能受到启发。只要自己刻苦钻研,就没有什么事情能够难倒你们。
上次的程序是建立在win32控制台上的。这个程序只是我研究迷宫自动生成算法的时候使用的。现在算法打通了,我们就应该使用图形化编程来使我们的迷宫做得更漂亮些。首先大家看看我迷宫的预览图吧。

怎么样,比以前那个控制台的要好多了吧。为了这个效果,我没有少花心思。这个迷宫除了能够电脑自动随机生成以外,还能显示当前迷宫的大小(大侠就见笑了,因为这些工作很容易就能搞定的)。图形化的程序借鉴的是我常常看的那本DirectX书上的例子程序。这些程序使我的开发周期缩短了很多。但是遗憾的是,这些函数我到现在还没有掌握该如何使用。姑且先来个移花接木吧。
首先看看我这个项目的文件示意图吧。


以下是我这个项目的文件之间的关系。


好了,现在该开始和大家说说我这个迷宫是如何实现的吧。首先的栈结构JStackDefine.h和JStackDefine.cpp文件没有作任何改动。随后因为考虑到代码精简的原因,我删除了一些头文件。像MazeDefine.h就没有了。MazeAutoCreate.h和MazeAutoCreate.cpp也精简了许多。那么首先从这些文件说起吧。


这里迷宫的基本结构是没有改变的,为了调用的方便,我在这个文件中加入了我的定点结构。它包含xyz坐标和rhw属性,另外还有颜色的属性。以后为了判断是墙还是道路,可以用不同的颜色来表示。另外,迷宫的行列也可以自己定义。到时候只需要用改变宏就可以对迷宫的大小进行更改(特别注意:迷宫的大小不能过大,因为会出现堆栈溢出的现象。我试了试105×63的还是可以的,但是是69×115的就会出现堆栈溢出的错误)。另外将GetRandom()函数声明为内联函数,可以加快程序的运行速度。

Code:
  1. #ifndef_J_MAZEAUTOCREATE_H_
  2. #define_J_MAZEAUTOCREATE_H_
  3. //迷宫的基本结构
  4. structMaze
  5. {
  6. inti,j;
  7. intstate;
  8. };
  9. //我们自定义的顶点结构
  10. structstD3DMaze
  11. {
  12. voidAssign(floatx_in,floaty_in,floatz_in,floatrhw_in,unsignedlongcolor_in)
  13. {
  14. x=x_in;
  15. y=y_in;
  16. z=z_in;
  17. color=color_in;
  18. }
  19. floatx,y,z,rhw;
  20. unsignedlongcolor;
  21. };
  22. //定义迷宫的行、列
  23. #defineM7
  24. #defineN11
  25. //我们自定义的FVF,表示顶点有位置,而且经过了矩阵变换,而且是有漫反射颜色的
  26. #defineD3DFVF_VERTEX(D3DFVF_XYZRHW|D3DFVF_DIFFUSE)
  27. voidMazeAutoCreate(Mazem[][N]);
  28. voidMazeInitialize(Mazem[][N]);
  29. inlineintGetRandom(intseed);
  30. voidRandomPath(Mazem[][N]);
  31. #endif

接下来就是我的MazeAutoCreate.cpp文件了。

Code:
  1. #include<ctime>
  2. #include"JStackDefine.h"
  3. #include"JStackDefine.cpp"//Essentialbecausetemplatefunctionbodyneedscalling.
  4. #include"MazeAutoCreate.h"
  5. voidMazeAutoCreate(Mazem[][N])
  6. {
  7. //首先选择一条路
  8. MazeInitialize(m);
  9. RandomPath(m);
  10. }
  11. voidMazeInitialize(Mazem[][N])
  12. {
  13. inti,j;
  14. for(i=0;i<M;i++)
  15. for(j=0;j<N;j++)
  16. {
  17. m[i][j].i=i;
  18. m[i][j].j=j;
  19. }
  20. }
  21. inlineintGetRandom(intseed)
  22. {
  23. return(int)time(NULL)/seed%4;
  24. }
  25. voidRandomPath(Mazem[][N])
  26. {
  27. JStack<Maze>mStack;//对堆栈进行实例化
  28. inti=(M/3)*2,j=(N/3)*2;
  29. intr=3;//种子的初始值,随后它会自加,从而保证随机性
  30. Mazetemp={i,j,0};//临时的Maze结构
  31. boollock[4]={false,false,false,false};//四个方向的锁
  32. m[i][j].state=1;
  33. mStack.Push(temp);
  34. while(1)
  35. {
  36. temp.i=i,temp.j=j;
  37. switch(GetRandom(r++))
  38. {
  39. case0://向上
  40. if(lock[0]==false//是否被锁住
  41. &&i>1/*是否越界*/
  42. &&m[i-2][j].state!=1/*隔一块是否为空*/
  43. &&mStack.GetTop().i!=i-2)/*是否走回头路*/
  44. {mStack.Push(temp);m[i-1][j].state=1;m[i-2][j].state=1;i-=2;lock[0]=false,lock[1]=false,lock[2]=false,lock[3]=false;/*Movebacktofalse*/}
  45. elselock[0]=true;
  46. break;
  47. case1://向下
  48. if(lock[1]==false//是否被锁住
  49. &&i<M-2/*是否越界*/
  50. &&m[i+2][j].state!=1/*隔一块是否为空*/
  51. &&mStack.GetTop().i!=i+2)/*是否走回头路*/
  52. {mStack.Push(temp);m[i+1][j].state=1;m[i+2][j].state=1;i+=2;lock[0]=false,lock[1]=false,lock[2]=false,lock[3]=false;/*Movebacktofalse*/}
  53. elselock[1]=true;
  54. break;
  55. case2://向左
  56. if(lock[2]==false//是否被锁住
  57. &&j>1/*是否越界*/
  58. &&m[i][j-2].state!=1/*隔一块是否为空*/
  59. &&mStack.GetTop().j!=j-2)/*是否走回头路*/
  60. {mStack.Push(temp);m[i][j-1].state=1;m[i][j-2].state=1;j-=2;lock[0]=false,lock[1]=false,lock[2]=false,lock[3]=false;/*Movebacktofalse*/}
  61. elselock[2]=true;
  62. break;
  63. case3://向右
  64. if(lock[3]==false//是否被锁住
  65. &&j<N-2/*是否越界*/
  66. &&m[i][j+2].state!=1/*隔一块是否为空*/
  67. &&mStack.GetTop().j!=j+2)/*是否走回头路*/
  68. {mStack.Push(temp);m[i][j+1].state=1;m[i][j+2].state=1;j+=2;lock[0]=false,lock[1]=false,lock[2]=false,lock[3]=false;/*Movebacktofalse*/}
  69. elselock[3]=true;
  70. break;
  71. }
  72. if(lock[0]==true&&lock[1]==true&&lock[2]==true&&lock[3]==true)
  73. {
  74. if(mStack.IsEmpty()==true)
  75. {
  76. m[0][0].state=m[M-1][N-1].state=2;//将入口和出口标出
  77. return;
  78. }
  79. else
  80. {
  81. i=mStack.GetTop().i;
  82. j=mStack.GetTop().j;
  83. mStack.Pop();
  84. lock[0]=false,lock[1]=false,lock[2]=false,lock[3]=false;//解锁
  85. }
  86. }
  87. }
  88. }


由于没有了控制台显示,所以MazeShow()函数就没有了。另外,由于文件调用的缘故,我这里没有使用全局变量,而是使用了传二维数组这种方式。但是就是这个使我花费了大量的时间。我还借了《标准c++宝典》来仔细研究怎样传二维数组的地址。看来自己的底子还是不行啊。

接下来介绍的是DirectRender.h文件。这个文件把一些需要添加的头文件、链接的库都列了出来,并且定义了窗口的宽和高。还有一些函数的声明。如下图所示。

Code:
  1. #ifndef_J_DIRECTRENDER_H_
  2. #define_J_DIRECTRENDER_H_
  3. //需要包含DirectX的头文件
  4. #include<d3d9.h>
  5. #include<d3dx9.h>
  6. #pragmacomment(lib,"d3d9.lib")
  7. #pragmacomment(lib,"d3dx9.lib")
  8. //窗口以及缓存的大小
  9. #defineWIDTH640
  10. #defineHEIGHT480
  11. //函数的声明
  12. boolInitializeD3D(HWNDhWnd,boolfullscreen);
  13. boolInitializeMaze(void);
  14. voidRenderScene(void);
  15. voidShutdown(void);
  16. #endif

于此对应的就是DirectRender.cpp文件了。这个文件是我最近三天修改次数最多的文件了。这个文件主要负责绘图。

Code:
  1. #include<stdio.h>//要调用sprintf函数
  2. #include"DirectRender.h"
  3. #include"MazeAutoCreate.h"
  4. //Direct3D的对象和设备
  5. LPDIRECT3D9g_D3D=NULL;
  6. LPDIRECT3DDEVICE9g_D3DDevice=NULL;
  7. //顶点缓存,用来装载几何图形
  8. LPDIRECT3DVERTEXBUFFER9g_VertexBuffer=NULL;
  9. //DirectX字体全局变量
  10. LPD3DXFONTg_DemoFont=NULL;
  11. RECTg_DemoFontPosition={0,0,0,0};
  12. boolInitializeD3D(HWNDhWnd,boolfullscreen)
  13. {
  14. D3DDISPLAYMODEdisplayMode;
  15. //创建D3D对象
  16. g_D3D=Direct3DCreate9(D3D_SDK_VERSION);
  17. if(g_D3D==NULL)returnfalse;
  18. //获取计算机显示的模式
  19. if(FAILED(g_D3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,
  20. &displayMode)))returnfalse;
  21. //为创建D3D设备做好准备
  22. D3DPRESENT_PARAMETERSd3dpp;
  23. ZeroMemory(&d3dpp,sizeof(d3dpp));
  24. //全屏选项
  25. if(fullscreen)
  26. {
  27. d3dpp.Windowed=FALSE;
  28. d3dpp.BackBufferWidth=WIDTH;
  29. d3dpp.BackBufferHeight=HEIGHT;
  30. }
  31. else
  32. d3dpp.Windowed=TRUE;
  33. //设置后台缓存
  34. d3dpp.SwapEffect=D3DSWAPEFFECT_DISCARD;
  35. d3dpp.BackBufferFormat=displayMode.Format;
  36. //创建D3D设备
  37. if(FAILED(g_D3D->CreateDevice(D3DADAPTER_DEFAULT,
  38. D3DDEVTYPE_HAL,hWnd,D3DCREATE_SOFTWARE_VERTEXPROCESSING,
  39. &d3dpp,&g_D3DDevice)))returnfalse;
  40. //创建需要进行显示的对象
  41. if(!InitializeMaze())returnfalse;
  42. returntrue;
  43. }
  44. //将迷宫数据m[M][N]转换成顶点缓存的函数
  45. voidTranslateToVertex(Mazem[][N],stD3DMaze*pObj)
  46. {
  47. //规定每一块的宽度和高度
  48. floatperWidth=WIDTH*0.78125f/N;
  49. floatperHeight=HEIGHT*0.625f/M;
  50. //规定起始方块的位置
  51. floatstartX=WIDTH*0.109f;
  52. floatstartY=HEIGHT*0.1875f;
  53. unsignedlongcolor;
  54. inti,j;
  55. for(i=0;i<M;i++)
  56. {
  57. for(j=0;j<N;j++)
  58. {
  59. switch(m[i][j].state)
  60. {//判断颜色
  61. case0:color=D3DCOLOR_XRGB(255,255,255);break;
  62. case1:color=D3DCOLOR_XRGB(80,80,80);break;
  63. case2:color=D3DCOLOR_XRGB(180,0,0);break;
  64. }//开始转换为顶点格式
  65. pObj->Assign(startX,startY,0,1,color);pObj++;
  66. pObj->Assign(startX+perWidth,startY,0,1,color);pObj++;
  67. pObj->Assign(startX,startY+perHeight,0,1,color);pObj++;
  68. pObj->Assign(startX,startY+perHeight,0,1,color);pObj++;
  69. pObj->Assign(startX+perWidth,startY,0,1,color);pObj++;
  70. pObj->Assign(startX+perWidth,startY+perHeight,0,1,color);pObj++;
  71. startX+=perWidth;
  72. }
  73. startX=WIDTH*0.109f;
  74. startY+=perHeight;
  75. }
  76. }
  77. //初始化对象的函数
  78. boolInitializeMaze(void)
  79. {
  80. //创建字体
  81. if(FAILED(D3DXCreateFont(g_D3DDevice,26,0,0,0,0,
  82. DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,DEFAULT_QUALITY,
  83. DEFAULT_PITCH|FF_DONTCARE,"黑体",
  84. &g_DemoFont)))returnfalse;
  85. //设置字体的位置
  86. g_DemoFontPosition.top=0;
  87. g_DemoFontPosition.left=0;
  88. g_DemoFontPosition.right=WIDTH;
  89. g_DemoFontPosition.bottom=HEIGHT;
  90. //这里定义了maze类的对象
  91. stD3DMazeobjData[M*N*6]={0};
  92. Mazem[M][N]={0};
  93. //自动创建迷宫,把迷宫的值放在m[M][N]数组上
  94. MazeAutoCreate(m);
  95. //将迷宫数据m[M][N]转换成顶点缓存
  96. TranslateToVertex(m,objData);
  97. //创建顶点缓存
  98. if(FAILED(g_D3DDevice->CreateVertexBuffer(sizeof(objData),0,
  99. D3DFVF_VERTEX,D3DPOOL_DEFAULT,&g_VertexBuffer,
  100. NULL)))returnfalse;
  101. //将顶点缓存注入内存中
  102. void*ptr;
  103. if(FAILED(g_VertexBuffer->Lock(0,sizeof(objData),
  104. (void**)&ptr,0)))returnfalse;
  105. //数据的复制
  106. memcpy(ptr,objData,sizeof(objData));
  107. g_VertexBuffer->Unlock();
  108. returntrue;
  109. }
  110. //渲染场景的函数
  111. voidRenderScene(void)
  112. {
  113. charstr[50];
  114. sprintf(str,"迷宫大小:%d×%d",N,M);
  115. //将后台缓存置为空
  116. g_D3DDevice->Clear(0,NULL,D3DCLEAR_TARGET,
  117. D3DCOLOR_XRGB(0,0,0),1.0f,0);
  118. //开始渲染场景
  119. g_D3DDevice->BeginScene();
  120. //设置字体的位置并显示出来
  121. g_DemoFontPosition.top=32;
  122. g_DemoFont->DrawText(NULL,"我的游戏:走迷宫蒋轶民制作",
  123. -1,&g_DemoFontPosition,DT_CENTER,
  124. D3DCOLOR_XRGB(185,220,0));
  125. g_DemoFontPosition.top=60;
  126. g_DemoFont->DrawText(NULL,str,
  127. -1,&g_DemoFontPosition,DT_CENTER,
  128. D3DCOLOR_XRGB(185,220,0));
  129. //渲染物体
  130. g_D3DDevice->SetStreamSource(0,g_VertexBuffer,0,
  131. sizeof(stD3DMaze));
  132. g_D3DDevice->SetFVF(D3DFVF_VERTEX);
  133. g_D3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST,0,M*N*2);
  134. //场景结束,停止渲染
  135. g_D3DDevice->EndScene();
  136. //显示场景
  137. g_D3DDevice->Present(NULL,NULL,NULL,NULL);
  138. }
  139. //释放空间的函数
  140. voidShutdown(void)
  141. {
  142. //释放空间
  143. if(g_D3DDevice!=NULL)g_D3DDevice->Release();
  144. if(g_D3D!=NULL)g_D3D->Release();
  145. if(g_VertexBuffer!=NULL)g_VertexBuffer->Release();
  146. //将指针置为空
  147. g_D3DDevice=NULL;
  148. g_D3D=NULL;
  149. g_VertexBuffer=NULL;
  150. }

使用sprintf()函数,为的是把数字类型(整型、实型等)转化为字符串类型,所以需要包含stdio.h文件。然后是与D3D有关的全局变量。之后就是初始化D3D函数InitializeD3D()、创建Maze对象函数InitializeMaze()、渲染场景函数RenderScene()和释放空间函数Shutdown()。其中有一个我刚刚没有介绍的,这个是我自己编辑的函数:TranslateToVertex()。这个函数的作用是将迷宫数据m[M][N]转换成顶点缓存。其实不要小看一个7×11的迷宫,其实它要渲染7×11×2个三角形,有7×11×6个顶点数据!试想要是69×115的迷宫要渲染多少个三角形,有多少个顶点。所以堆栈溢出那是必然的了。除非你的计算机的配置很高,要不然还是不要折磨你的计算机了,毕竟进行栈的操作效率比较低。
另外说明一下,使用一系列的宏和一系列的计算,我们可以确保在迷宫的大小改变的情况下不改变渲染区域的大小。这就要数学学得好的人花工夫了。所以这些运算
“// 规定每一块的宽度和高度
float perWidth = WIDTH*0.78125f/N;
float perHeight = HEIGHT*0.625f/M;
// 规定起始方块的位置
float startX = WIDTH*0.109f;
float startY = HEIGHT*0.1875f;”是值得的。


然后就是MainFrame.h文件。这个文件比较简单,只有一条简单的包含语句。

Code:
  1. #ifndef_J_MAINFRAME_H_
  2. #define_J_MAINFRAME_H_
  3. //包含DirectRender.h头文件
  4. #include"DirectRender.h"
  5. #endif


然后就是我们的MainFrame.cpp文件了。这个cpp文件包含了消息处理函数和主函数。这里除了自己添加的是否全屏的选项外,其余的和书上的例子程序无异。文件的代码如图所示。

Code:
  1. //蒋轶民制作E-mail:jiangcaiyang123@163.com
  2. //Thisisademoconfirminghowdirectdrawworks
  3. #include<windows.h>
  4. #include"MainFrame.h"
  5. #defineAPPCLASS"我的DirectX迷宫"
  6. #defineWINDOW_TITLE"我的DirectX迷宫(一)"
  7. LRESULTWINAPIMsgProc(HWNDhWnd,UINTmsg,WPARAMwp,LPARAMlp)
  8. {
  9. switch(msg)
  10. {
  11. caseWM_DESTROY:
  12. PostQuitMessage(0);
  13. break;
  14. caseWM_KEYUP:
  15. if(wp==VK_ESCAPE)PostQuitMessage(0);
  16. break;
  17. }
  18. returnDefWindowProc(hWnd,msg,wp,lp);
  19. }
  20. intWINAPIWinMain(HINSTANCEhInst,HINSTANCEprevhInst,LPSTRcmdLine,intshow)
  21. {
  22. //注册窗口类
  23. WNDCLASSEXwc={sizeof(WNDCLASSEX),CS_CLASSDC,MsgProc,
  24. 0,0,GetModuleHandle(NULL),NULL,NULL,
  25. NULL,NULL,APPCLASS,NULL};
  26. RegisterClassEx(&wc);
  27. //创建窗口
  28. HWNDhWnd=CreateWindow(APPCLASS,WINDOW_TITLE,
  29. WS_OVERLAPPEDWINDOW,100,50,
  30. WIDTH,HEIGHT,GetDesktopWindow(),NULL,
  31. wc.hInstance,NULL);
  32. boolfullscreen=false;
  33. if(MessageBox(NULL,"是否进行全屏显示?",WINDOW_TITLE,MB_ICONQUESTION|MB_YESNO)==IDYES)
  34. {
  35. fullscreen=true;//全屏显示
  36. ShowCursor(false);//隐藏鼠标指针
  37. }
  38. //显示窗口
  39. ShowWindow(hWnd,SW_SHOWDEFAULT);
  40. UpdateWindow(hWnd);
  41. //初始化Direct3D
  42. if(InitializeD3D(hWnd,fullscreen))
  43. {
  44. //进入消息循环
  45. MSGmsg;
  46. ZeroMemory(&msg,sizeof(msg));
  47. while(msg.message!=WM_QUIT)
  48. {
  49. if(PeekMessage(&msg,NULL,0U,0U,PM_REMOVE))
  50. {
  51. TranslateMessage(&msg);
  52. DispatchMessage(&msg);
  53. }
  54. else
  55. {
  56. RenderScene();
  57. }
  58. }
  59. }
  60. //解除注册窗口
  61. UnregisterClass(APPCLASS,wc.hInstance);
  62. return0;
  63. }

测试运行:首先是一个消息对话框,提示是否全屏。

然后就是我们的迷宫了,渲染的时间有1到2秒左右吧,差强人意。看看D3D的游戏速度很快,说明我的代码还有优化的空间。


好了,在苦苦挣扎10余天后,我终于用DirectX把自己的迷宫编辑出来了。当编辑出来的时候,那时真的很自豪。我恨不得把我的成果告诉所有我认识的人。现在也是不敢把自己的成果独享,所以将自己的心得写成日志,希望大家能有所启发。谢谢赏阅

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值