当前位置:   article > 正文

SimpleCG小游戏开发系列(2)--贪吃蛇

SimpleCG小游戏开发系列(2)--贪吃蛇

一、前言

        在之前的C语言小游戏开发系列我们已经介绍了扫雷游戏的开发,本篇我们继续此系列第二篇,同样是比较简单但好玩的一个游戏--贪吃蛇。因为有了之前的游戏框架,我们只需要直接搬来原来的框架即可,可以省去不少活。

先看看游戏效果预览

下面进行详细的逻辑介绍。

二、玩法介绍

        贪吃蛇是一款经典的游戏,玩家需要控制一条蛇在屏幕上移动,不断吃掉食物并成长,同时要避免碰到墙壁或自己的身体。蛇在移动过程中会遇到食物,玩家需要让蛇的头部碰到食物,蛇就会自动将食物吞下,并且蛇的长度会相应增加。碰到墙壁或自己的身体就游戏结束。

三、主要逻辑及难点说明

        贪吃蛇游戏的核心逻辑在于蛇的移动和食物的生成。蛇的移动需要精确控制,避免蛇的头部碰到自己的身体或者墙壁,食物的生成需要有一定的随机性,使得游戏有一定的变化性。

        我们首先将游戏分三个场景进行绘制,分别是开始前的菜单显示、游戏进行中的场景显示、以及游戏结束界面的显示。我们可以直接用一个全局整型变量g_nGameState来区分场景。在游戏进行时我们主要绘制蛇行走的地图,这个在前面扫雷游戏中有所介绍,大同小异,主要就是一个一维数组实现的二维平面。

int		g_nMap[ C_MAP_WIDTH * C_MAP_HEIGHT]		= {0};

然后是蛇的表示,我们用一个坐标数组来表示蛇身体所处地图位置。

POINT	g_ptSnake[C_SNAKE_MAXLEN];

 地图绘制就是根据地图表示的地形绘制出相应内容

  1. for(j=0;j<C_MAP_HEIGHT;++j)
  2. {
  3. for(i=0;i<C_MAP_WIDTH;++i)
  4. {
  5. switch( g_nMap[j*C_MAP_WIDTH+i] )
  6. {
  7. case enumMAPTYPE_STONE:
  8. solidrectangle( i*C_IMAGE_BLOCK, j*C_IMAGE_BLOCK, i*C_IMAGE_BLOCK+C_IMAGE_BLOCK, j*C_IMAGE_BLOCK+C_IMAGE_BLOCK);
  9. break;
  10. case enumMAPTYPE_FOOD:
  11. setfillcolor(RGB(0xAA,0x0,0x0));
  12. solidcircle( i*C_IMAGE_BLOCK+10, j*C_IMAGE_BLOCK+10, 5);
  13. setfillcolor(RGB(0xAA,0xAA,0xAA));
  14. break;
  15. }
  16. }
  17. }

蛇的行走就只需要根据当前方向进行坐标移动就可以了

  1. switch( g_nSnakDir )
  2. {
  3. case enumDIR_LEFT:
  4. SnakeMoveTo(g_ptSnake[0].x-1,g_ptSnake[0].y);
  5. break;
  6. case enumDIR_RIGHT:
  7. SnakeMoveTo(g_ptSnake[0].x+1,g_ptSnake[0].y);
  8. break;
  9. case enumDIR_UP:
  10. SnakeMoveTo(g_ptSnake[0].x,g_ptSnake[0].y-1);
  11. break;
  12. case enumDIR_DOWN:
  13. SnakeMoveTo(g_ptSnake[0].x,g_ptSnake[0].y+1);
  14. break;
  15. }

以上就是贪吃蛇涉及的主要设计难点,都比较容易理解,就不展开讨论了。

 四、主体逻辑的所有源代码

        以下就是贪吃蛇的全部主要代码,只是示例程序,所以只设置了一关、有兴趣的同学可以自行扩展代码。

  1. // GameSnake.cpp : 定义控制台应用程序的入口点。
  2. //
  3. #include "../import/include/SimpleCG.h"
  4. #include "GameUI.h"
  5. #ifdef _DEBUG
  6. #pragma comment(lib,"../import/lib/SimpleCG_MDd.lib")
  7. #else
  8. #ifdef _WIN64
  9. #pragma comment(lib,"../import/lib/x64/MTRelease/SimpleCG_MT64.lib")
  10. #else
  11. #pragma comment(lib,"../import/lib/SimpleCG_MT.lib")
  12. #endif
  13. #endif
  14. #define C_SNAK_VERSION 1001
  15. #define C_IMAGE_WIDTH 640
  16. #define C_IMAGE_HEIGHT 480
  17. #define C_IMAGE_BLOCK 20
  18. #define C_MAP_WIDTH C_IMAGE_WIDTH/C_IMAGE_BLOCK
  19. #define C_MAP_HEIGHT C_IMAGE_HEIGHT/C_IMAGE_BLOCK
  20. #define C_SNAKE_MAXLEN 50
  21. enum ENUM_GAMESTATE
  22. {
  23. enumGS_MENU
  24. , enumGS_RUNNING
  25. , enumGS_GAMEOVER
  26. };
  27. enum ENUM_MAPTYPE
  28. {
  29. enumMAPTYPE_NULL
  30. , enumMAPTYPE_STONE
  31. , enumMAPTYPE_FOOD
  32. };
  33. enum ENUM_DIRECTION
  34. {
  35. enumDIR_LEFT
  36. , enumDIR_RIGHT
  37. , enumDIR_UP
  38. , enumDIR_DOWN
  39. };
  40. int g_nMap[ C_MAP_WIDTH * C_MAP_HEIGHT] = {0};
  41. int g_nSnakDir = enumDIR_LEFT;
  42. POINT g_ptSnake[C_SNAKE_MAXLEN];
  43. int g_nSnakLen = 2;
  44. int g_nGameState = enumGS_MENU;
  45. char g_nTitle[] = { 0x4f,0x49,0x49,0x79,0x0,0x7f,0xe,0x30,0x7f,0x0,0x7e,0x9,0x9,0x7e,0x0,0x7f,0x8,0x14,0x63,0x0,0x7f,0x49,0x49,0x49 };
  46. char g_pGame[] = { 0x7f,0x41,0x5d,0x49,0x7b,0x0,0x7c,0x12,0x11,0x12,0x7c,0x0,0x7f,0x2,0x4,0x2,0x7f,0x0,0x7f,0x49,0x49,0x49,0x49 };
  47. char g_pOver[] = { 0x7f,0x41,0x41,0x41,0x7f,0x0,0x1f,0x20,0x40,0x20,0x1f,0x0,0x7f,0x49,0x49,0x49,0x49,0x0,0x7f,0x9,0x19,0x29,0x46 };
  48. int g_nMenu = 0;
  49. int g_nScore = 0;
  50. int g_nSpeed = 1;
  51. LRESULT OnKeyDown(HWND hWnd, WPARAM wParam, LPARAM lParam);
  52. LRESULT OnLButtonDown( HWND hWnd, WPARAM wParam, int nX, int nY );
  53. LRESULT OnRButtonDown( HWND hWnd, WPARAM wParam, int nX, int nY );
  54. LRESULT OnMouseMove( HWND hWnd, WPARAM wParam, int nX, int nY );
  55. //绘制标题
  56. void DrawTitle( int nX, int nY, char *pTitle, int nSize);
  57. void MakeFood()
  58. {
  59. bool bFoodSet = false;
  60. int nRand = 0;
  61. while(!bFoodSet)
  62. {
  63. nRand=rand()%(C_MAP_WIDTH*C_MAP_HEIGHT);
  64. if(g_nMap[nRand] == enumMAPTYPE_NULL )
  65. {
  66. g_nMap[nRand] = enumMAPTYPE_FOOD;
  67. bFoodSet = true;
  68. }
  69. }
  70. }
  71. //初始化游戏
  72. void InitGame(HWND hWnd)
  73. {
  74. int i=0;
  75. int j=0;
  76. for(j=0;j<C_MAP_HEIGHT;++j)
  77. {
  78. for(i=0;i<C_MAP_WIDTH;++i)
  79. {
  80. if(i==0||i==C_MAP_WIDTH-1||j==0||j==C_MAP_HEIGHT-1)
  81. {
  82. g_nMap[j*C_MAP_WIDTH+i] = enumMAPTYPE_STONE;
  83. }
  84. else
  85. {
  86. g_nMap[j*C_MAP_WIDTH+i] = enumMAPTYPE_NULL;
  87. }
  88. }
  89. }
  90. g_nSpeed = 1;
  91. g_nSnakLen = 2;
  92. g_ptSnake[0].x = 10;
  93. g_ptSnake[0].y = 10;
  94. g_ptSnake[1].x = 11;
  95. g_ptSnake[1].y = 10;
  96. g_nSnakDir = enumDIR_LEFT;
  97. srand(GetTickCount());
  98. MakeFood();
  99. //按键处理初始化
  100. //SetKeyboardProcess( enumINMSG_KEYDOWN, OnKeyDown );
  101. GetKeyboardInput()->onKeyDown = OnKeyDown;
  102. GetMouseInput()->onMouseDownLeft = OnLButtonDown;
  103. GetMouseInput()->onMouseDownRight = OnRButtonDown;
  104. GetMouseInput()->onMouseMove = OnMouseMove;
  105. g_nGameState = enumGS_MENU;
  106. }
  107. //移动蛇
  108. void SnakeMoveTo(int x, int y)
  109. {
  110. int i=0;
  111. if(x<0 || y<0 || x>=C_MAP_WIDTH || y>=C_MAP_HEIGHT )
  112. return;
  113. //GetFood
  114. if(g_nMap[y*C_MAP_WIDTH+x]==enumMAPTYPE_FOOD)
  115. {
  116. g_nMap[y*C_MAP_WIDTH+x]=enumMAPTYPE_NULL;
  117. g_ptSnake[g_nSnakLen].x = g_ptSnake[g_nSnakLen-1].x;
  118. g_ptSnake[g_nSnakLen].y = g_ptSnake[g_nSnakLen-1].y;
  119. if( g_nSnakLen<C_SNAKE_MAXLEN-1)
  120. g_nSnakLen++;
  121. g_nScore += 10 * g_nSpeed;
  122. g_nSpeed = g_nSnakLen / 5 + 1;
  123. MakeFood();
  124. }
  125. else if(g_nMap[y*C_MAP_WIDTH+x]==enumMAPTYPE_STONE )//dead
  126. {
  127. g_nGameState = enumGS_GAMEOVER;
  128. return;
  129. }
  130. for(i=g_nSnakLen-1;i>0;--i)
  131. {
  132. if( g_ptSnake[i].x==x && g_ptSnake[i].y==y )
  133. {
  134. g_nGameState = enumGS_GAMEOVER;
  135. return;
  136. }
  137. g_ptSnake[i].x = g_ptSnake[i-1].x;
  138. g_ptSnake[i].y = g_ptSnake[i-1].y;
  139. }
  140. g_ptSnake[0].x = x;
  141. g_ptSnake[0].y = y;
  142. }
  143. //按方向移动
  144. void MoveFoward()
  145. {
  146. switch( g_nSnakDir )
  147. {
  148. case enumDIR_LEFT:
  149. SnakeMoveTo(g_ptSnake[0].x-1,g_ptSnake[0].y);
  150. break;
  151. case enumDIR_RIGHT:
  152. SnakeMoveTo(g_ptSnake[0].x+1,g_ptSnake[0].y);
  153. break;
  154. case enumDIR_UP:
  155. SnakeMoveTo(g_ptSnake[0].x,g_ptSnake[0].y-1);
  156. break;
  157. case enumDIR_DOWN:
  158. SnakeMoveTo(g_ptSnake[0].x,g_ptSnake[0].y+1);
  159. break;
  160. }
  161. }
  162. //更新游戏状态
  163. void UpdateGame( UINT nElapse )
  164. {
  165. static int s_nLastTick = 0;
  166. int nWait = 9-g_nSpeed;
  167. if(nWait<0)
  168. nWait = 1;
  169. if( ( nElapse - s_nLastTick )<nWait*50 )
  170. return;
  171. switch(g_nGameState)
  172. {
  173. case enumGS_MENU:
  174. break;
  175. case enumGS_RUNNING:
  176. s_nLastTick = nElapse;
  177. MoveFoward();
  178. break;
  179. case enumGS_GAMEOVER:
  180. break;
  181. }
  182. }
  183. //绘制地图
  184. void DrawMap()
  185. {
  186. int i=0;
  187. int j=0;
  188. setfillcolor(RGB(0xAA,0xAA,0xAA));
  189. for(j=0;j<C_MAP_HEIGHT;++j)
  190. {
  191. for(i=0;i<C_MAP_WIDTH;++i)
  192. {
  193. switch( g_nMap[j*C_MAP_WIDTH+i] )
  194. {
  195. case enumMAPTYPE_STONE:
  196. solidrectangle( i*C_IMAGE_BLOCK, j*C_IMAGE_BLOCK, i*C_IMAGE_BLOCK+C_IMAGE_BLOCK, j*C_IMAGE_BLOCK+C_IMAGE_BLOCK);
  197. break;
  198. case enumMAPTYPE_FOOD:
  199. setfillcolor(RGB(0xAA,0x0,0x0));
  200. solidcircle( i*C_IMAGE_BLOCK+10, j*C_IMAGE_BLOCK+10, 5);
  201. setfillcolor(RGB(0xAA,0xAA,0xAA));
  202. break;
  203. }
  204. }
  205. }
  206. }
  207. //绘制标题
  208. void DrawTitle( int nX, int nY, char *pTitle, int nSize)
  209. {
  210. int i=0;
  211. int j=0;
  212. for(i=0;i<nSize;++i)
  213. {
  214. int nNum = pTitle[i];
  215. for(j=0;j<8;++j)
  216. {
  217. if(nNum&0x1)
  218. {
  219. solidrectangle( nX+i*C_IMAGE_BLOCK, nY+j*C_IMAGE_BLOCK, nX+i*C_IMAGE_BLOCK+C_IMAGE_BLOCK, nY+j*C_IMAGE_BLOCK+C_IMAGE_BLOCK);
  220. }
  221. nNum>>=1;
  222. }
  223. }
  224. }
  225. //绘制蛇
  226. void DrawSnake()
  227. {
  228. int i=0;
  229. setfillcolor(RGB(0x0,0xAA,0x0));
  230. for(i=0; i<g_nSnakLen; ++i)
  231. {
  232. solidrectangle( g_ptSnake[i].x*C_IMAGE_BLOCK, g_ptSnake[i].y*C_IMAGE_BLOCK, g_ptSnake[i].x*C_IMAGE_BLOCK+C_IMAGE_BLOCK, g_ptSnake[i].y*C_IMAGE_BLOCK+C_IMAGE_BLOCK);
  233. }
  234. }
  235. void DrawMenu()
  236. {
  237. setfillcolor(RGB(0x0,0xAA,0x0));
  238. DrawTitle( 60, 60, g_nTitle, sizeof(g_nTitle)/sizeof(g_nTitle[0]));
  239. DrawButton( 200, 260, 120, 30, RGB(0x0,0xDD,0x0), 0, _T("开始游戏"));
  240. DrawButton( 200, 300, 120, 30, RGB(0x0,0xDD,0x0), 0, _T("退出游戏"));
  241. setlinecolor(0);
  242. fillcircle( 180, g_nMenu * 40 + 260 + 15, 10 );
  243. }
  244. //绘制游戏
  245. void RenderGame()
  246. {
  247. clearrectangle(0,0,C_IMAGE_WIDTH, C_IMAGE_HEIGHT);
  248. switch(g_nGameState)
  249. {
  250. case enumGS_MENU:
  251. DrawMenu();
  252. break;
  253. case enumGS_RUNNING:
  254. DrawMap();
  255. DrawSnake();
  256. RenderUI();
  257. break;
  258. case enumGS_GAMEOVER:
  259. DrawTitle( 100, 60, g_pGame, sizeof(g_pGame)/sizeof(g_pGame[0]));
  260. DrawTitle( 100, 220, g_pOver, sizeof(g_pOver)/sizeof(g_pOver[0]));
  261. settextcolor(0);
  262. printfRectEx( 0, 380, C_IMAGE_WIDTH, 20, SCG_TEXT_MIDDLE, _T("分数: %d"), g_nScore );
  263. //outtextRectEx( 0, 400, C_IMAGE_WIDTH, 100, _T("Press any key to continue..."), SCG_TEXT_MIDDLE);
  264. outtextRectEx( 0, 400, C_IMAGE_WIDTH, 100, _T("按任意键继续..."), SCG_TEXT_MIDDLE);
  265. break;
  266. }
  267. }
  268. //按键消息响应函数
  269. LRESULT OnKeyDown(HWND hWnd, WPARAM wParam, LPARAM lParam)
  270. {
  271. switch( g_nGameState )
  272. {
  273. case enumGS_MENU:
  274. switch( wParam )
  275. {
  276. case VK_DOWN:
  277. if( g_nMenu<1 )
  278. ++g_nMenu;
  279. break;
  280. case VK_UP:
  281. if( g_nMenu> 0 )
  282. --g_nMenu;
  283. break;
  284. case VK_SPACE:
  285. case VK_RETURN:
  286. if( g_nMenu == 1)
  287. PostQuitMessage(0);
  288. else
  289. g_nGameState = enumGS_RUNNING;
  290. break;
  291. }
  292. break;
  293. case enumGS_RUNNING:
  294. switch( wParam )
  295. {
  296. case VK_DOWN:
  297. if(g_nSnakDir == enumDIR_LEFT || g_nSnakDir == enumDIR_RIGHT)
  298. {
  299. g_nSnakDir = enumDIR_DOWN;
  300. MoveFoward();
  301. }
  302. return 1;
  303. break;
  304. case VK_UP:
  305. if(g_nSnakDir == enumDIR_LEFT || g_nSnakDir == enumDIR_RIGHT)
  306. {
  307. g_nSnakDir = enumDIR_UP;
  308. MoveFoward();
  309. }
  310. return 1;
  311. break;
  312. case VK_LEFT:
  313. if(g_nSnakDir == enumDIR_UP || g_nSnakDir == enumDIR_DOWN)
  314. {
  315. g_nSnakDir = enumDIR_LEFT;
  316. MoveFoward();
  317. }
  318. return 1;
  319. break;
  320. case VK_RIGHT:
  321. if(g_nSnakDir == enumDIR_UP || g_nSnakDir == enumDIR_DOWN)
  322. {
  323. g_nSnakDir = enumDIR_RIGHT;
  324. MoveFoward();
  325. }
  326. return 1;
  327. break;
  328. }
  329. break;
  330. case enumGS_GAMEOVER:
  331. InitGame(NULL);
  332. break;
  333. }
  334. return 0;
  335. }
  336. LRESULT OnLButtonDown( HWND hWnd, WPARAM wParam, int nX, int nY )
  337. {
  338. switch( g_nGameState )
  339. {
  340. case enumGS_MENU:
  341. if( g_nMenu == 1)
  342. PostQuitMessage(0);
  343. else
  344. g_nGameState = enumGS_RUNNING;
  345. break;
  346. case enumGS_RUNNING:
  347. switch(g_nSnakDir )
  348. {
  349. case enumDIR_LEFT:
  350. g_nSnakDir = enumDIR_DOWN;
  351. break;
  352. case enumDIR_RIGHT:
  353. g_nSnakDir = enumDIR_UP;
  354. break;
  355. case enumDIR_UP:
  356. g_nSnakDir = enumDIR_LEFT;
  357. break;
  358. case enumDIR_DOWN:
  359. g_nSnakDir = enumDIR_RIGHT;
  360. break;
  361. }
  362. break;
  363. case enumGS_GAMEOVER:
  364. InitGame(NULL);
  365. break;
  366. }
  367. return false;
  368. }
  369. LRESULT OnRButtonDown( HWND hWnd, WPARAM wParam, int nX, int nY )
  370. {
  371. if( g_nGameState == enumGS_RUNNING)
  372. {
  373. switch(g_nSnakDir )
  374. {
  375. case enumDIR_LEFT:
  376. g_nSnakDir = enumDIR_UP;
  377. break;
  378. case enumDIR_RIGHT:
  379. g_nSnakDir = enumDIR_DOWN;
  380. break;
  381. case enumDIR_UP:
  382. g_nSnakDir = enumDIR_RIGHT;
  383. break;
  384. case enumDIR_DOWN:
  385. g_nSnakDir = enumDIR_LEFT;
  386. break;
  387. }
  388. }
  389. return false;
  390. }
  391. LRESULT OnMouseMove( HWND hWnd, WPARAM wParam, int nX, int nY )
  392. {
  393. if( g_nGameState == enumGS_MENU)
  394. {
  395. if( nY>=300)
  396. g_nMenu = 1;
  397. else
  398. g_nMenu = 0;
  399. }
  400. return false;
  401. }
  402. int _tmain(int argc, _TCHAR* argv[])
  403. {
  404. SCG_GameLoopInfo info;
  405. info.nFPS = 60;
  406. info.nHeight = C_IMAGE_HEIGHT;
  407. info.nWidth = C_IMAGE_WIDTH;
  408. info.pFunDrawProcess = RenderGame;
  409. info.pFunFrameUpdate = UpdateGame;
  410. info.pFunInitGame = InitGame;
  411. info.pFunInput = NULL;
  412. if( !StartGameLoopEx( &info ))
  413. return 1;
  414. return 0;
  415. }

五、代码及相关图形库下载

贪吃蛇完整VS2010工程可在以下地址下载。

gamesnake · master · b2b160 / SimpleCG_Demo · GitCode

编译此程序需安装SimpleCG库,安装方法如下: 

SimpleCG库安装方法

如果只想执行程序可在如下地址下载 

贪吃蛇执行程序

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/372504
推荐阅读
相关标签
  

闽ICP备14008679号