当前位置:   article > 正文

c/c++ 实现俄罗斯方块小游戏(附全部源码)_c++代码小游戏

c++代码小游戏

目录


前言

使用easyX库,基于c/c++,实现俄罗斯方块小游戏。

作者使用的是VS2010版本,VS2019版本也可用。


一、游戏截图和全部代码

  • 1.游戏截图

  • 2.源代码

可直接拷贝,并运行俄罗斯方块小游戏

  • 头文件代码

  1. #pragma once
  2. #define GAMEMAP1 200 // 预生成方块区域
  3. #define GAMEMAP2 10
  4. #define GAMEMAP3 500
  5. #define GAMEMAP4 610
  6. #define GAMEMAP5 40 // 游戏地图区域
  7. #define GAMEMAP6 50
  8. #define GAMEMAP7 140
  9. #define GAMEMAP8 130
  10. #define GAMEGUIDE1 40 // 按键说明
  11. #define GAMEGUIDE2 300
  12. #define GAMEGUIDE3 150
  13. #define GAMEGUIDE4 440
  14. #define GAMELEVEL1 30 // 游戏等级区域
  15. #define GAMELEVEL2 160
  16. #define GAMERANK1 30 // 游戏rank分数
  17. #define GAMERANK2 190
  18. #define TETRISWIDTH 15 // 游戏地图宽
  19. #define TETRISHEIGHT 30 // 游戏地图高
  20. #define BLOCKTYPE 8 // 方块种类数
  21. #define BLOCKSTYLE 4 // 每种方块的样式数
  22. #define BLOCKSPACE 20 // 方块占用空间 5 x 4
  23. #define SIDELENGTH 20 // 方块边长 20 像素
  24. #define ESC 27
  25. #define CLEARKEY(); { while(_kbhit()) { _getch(); } } // 清除按键输入缓存
  26. enum BLOCKCOLER
  27. {
  28. BLOCKNONE,
  29. BLOCKBLUE,
  30. BLOCKGREEN,
  31. BLOCKCYAN,
  32. BLOCKRED,
  33. BLOCKMAGENTA,
  34. BLOCKBROWN,
  35. BLOCKYELLOW,
  36. };
  37. typedef struct _GAME_TETRIS
  38. {
  39. int ttime; // 计时
  40. int ctime; // 计时
  41. int level; // 当前游戏等级
  42. int rank; // 当前游戏分数
  43. int key; // 玩家按键值
  44. int blockType; // 当前方块类型
  45. int blockStyle; // 当前方块朝向
  46. bool moveFlag; // 移动标识,标识为1时,移动当前方块
  47. bool newBlockFlag; // 载入新方块标识,当标识为真时,将预生成的随机方块载入地图
  48. bool gameOverFlag; // 游戏结束标识
  49. }GAME_TETRIS;
  50. typedef struct _GAME_PREBLOCK
  51. {
  52. int tetrisPreBlock[BLOCKSPACE]; // 预生成的方块数组
  53. int blockType,blockStyle; // 预生成方块的类型和朝向
  54. }GAME_PREBLOCK;
  55. typedef struct _GAME_MOVE_BLOCK
  56. {
  57. int blockSite[4][2]; // 4个方块格坐标
  58. int blockColor; // 方块颜色
  59. }GAME_MOVE_BLOCK;
  60. void tetrisrun(void);
  61. void tetrisInit(void);
  62. void tetrisDraw(void);
  63. void tetrisNewBlock(void);
  64. void tetrisMoveUp(void);
  65. void tetrisMoveDown(void);
  66. void tetrisMoveLeft(void);
  67. void tetrisMoveRight(void);
  68. void tetrisLoadBlock(void);
  69. void tetrisIsOver(void);
  70. void tetrisQuit(void);
  71. void tetrisKeyHandle(void);
  72. void tetrisReset(void);
  73. void tetrisRemove(void);
  • cpp文件代码

  1. #include "stdafx.h"
  2. #include "tetrisnew.h"
  3. #include<graphics.h>
  4. #include<conio.h>
  5. #include<stdio.h>
  6. #include<time.h>
  7. // 所有方块数组库
  8. const int block[BLOCKTYPE][BLOCKSTYLE][BLOCKSPACE] =
  9. {
  10. // 总共有8种方块,每种方块有4种样式
  11. // 'I'形方块
  12. {
  13. {
  14. BLOCKNONE, BLOCKBLUE, BLOCKBLUE, BLOCKBLUE, BLOCKBLUE,
  15. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  16. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  17. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  18. },
  19. {
  20. BLOCKNONE, BLOCKNONE, BLOCKBLUE, BLOCKNONE, BLOCKNONE,
  21. BLOCKNONE, BLOCKNONE, BLOCKBLUE, BLOCKNONE, BLOCKNONE,
  22. BLOCKNONE, BLOCKNONE, BLOCKBLUE, BLOCKNONE, BLOCKNONE,
  23. BLOCKNONE, BLOCKNONE, BLOCKBLUE, BLOCKNONE, BLOCKNONE
  24. },
  25. {
  26. BLOCKBLUE, BLOCKBLUE, BLOCKBLUE, BLOCKBLUE, BLOCKNONE,
  27. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  28. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  29. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  30. },
  31. {
  32. BLOCKNONE, BLOCKNONE, BLOCKBLUE, BLOCKNONE, BLOCKNONE,
  33. BLOCKNONE, BLOCKNONE, BLOCKBLUE, BLOCKNONE, BLOCKNONE,
  34. BLOCKNONE, BLOCKNONE, BLOCKBLUE, BLOCKNONE, BLOCKNONE,
  35. BLOCKNONE, BLOCKNONE, BLOCKBLUE, BLOCKNONE, BLOCKNONE
  36. }
  37. },
  38. // 'Z'形方块
  39. {
  40. {
  41. BLOCKNONE, BLOCKGREEN, BLOCKGREEN, BLOCKNONE, BLOCKNONE,
  42. BLOCKNONE, BLOCKNONE, BLOCKGREEN, BLOCKGREEN, BLOCKNONE,
  43. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  44. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  45. },
  46. {
  47. BLOCKNONE, BLOCKNONE, BLOCKGREEN, BLOCKNONE, BLOCKNONE,
  48. BLOCKNONE, BLOCKGREEN, BLOCKGREEN, BLOCKNONE, BLOCKNONE,
  49. BLOCKNONE, BLOCKGREEN, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  50. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  51. },
  52. {
  53. BLOCKNONE, BLOCKGREEN, BLOCKGREEN, BLOCKNONE, BLOCKNONE,
  54. BLOCKNONE, BLOCKNONE, BLOCKGREEN, BLOCKGREEN, BLOCKNONE,
  55. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  56. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  57. },
  58. {
  59. BLOCKNONE, BLOCKNONE, BLOCKGREEN, BLOCKNONE, BLOCKNONE,
  60. BLOCKNONE, BLOCKGREEN, BLOCKGREEN, BLOCKNONE, BLOCKNONE,
  61. BLOCKNONE, BLOCKGREEN, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  62. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  63. }
  64. },
  65. //'Z'形方块
  66. {
  67. {
  68. BLOCKNONE, BLOCKNONE, BLOCKCYAN, BLOCKCYAN, BLOCKNONE,
  69. BLOCKNONE, BLOCKCYAN, BLOCKCYAN, BLOCKNONE, BLOCKNONE,
  70. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  71. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  72. },
  73. {
  74. BLOCKNONE, BLOCKNONE, BLOCKCYAN, BLOCKNONE, BLOCKNONE,
  75. BLOCKNONE, BLOCKNONE, BLOCKCYAN, BLOCKCYAN, BLOCKNONE,
  76. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKCYAN, BLOCKNONE,
  77. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  78. },
  79. {
  80. BLOCKNONE, BLOCKNONE, BLOCKCYAN, BLOCKCYAN, BLOCKNONE,
  81. BLOCKNONE, BLOCKCYAN, BLOCKCYAN, BLOCKNONE, BLOCKNONE,
  82. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  83. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  84. },
  85. {
  86. BLOCKNONE, BLOCKNONE, BLOCKCYAN, BLOCKNONE, BLOCKNONE,
  87. BLOCKNONE, BLOCKNONE, BLOCKCYAN, BLOCKCYAN, BLOCKNONE,
  88. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKCYAN, BLOCKNONE,
  89. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  90. }
  91. },
  92. // 'T'形方块
  93. {
  94. {
  95. BLOCKNONE, BLOCKNONE, BLOCKRED, BLOCKNONE, BLOCKNONE,
  96. BLOCKNONE, BLOCKRED, BLOCKRED, BLOCKRED, BLOCKNONE,
  97. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  98. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  99. },
  100. {
  101. BLOCKNONE, BLOCKNONE, BLOCKRED, BLOCKNONE, BLOCKNONE,
  102. BLOCKNONE, BLOCKNONE, BLOCKRED, BLOCKRED, BLOCKNONE,
  103. BLOCKNONE, BLOCKNONE, BLOCKRED, BLOCKNONE, BLOCKNONE,
  104. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  105. },
  106. {
  107. BLOCKNONE, BLOCKRED, BLOCKRED, BLOCKRED, BLOCKNONE,
  108. BLOCKNONE, BLOCKNONE, BLOCKRED, BLOCKNONE, BLOCKNONE,
  109. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  110. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  111. },
  112. {
  113. BLOCKNONE, BLOCKNONE, BLOCKRED, BLOCKNONE, BLOCKNONE,
  114. BLOCKNONE, BLOCKRED, BLOCKRED, BLOCKNONE, BLOCKNONE,
  115. BLOCKNONE, BLOCKNONE, BLOCKRED, BLOCKNONE, BLOCKNONE,
  116. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  117. }
  118. },
  119. // 'T'形方块 X 2 增加T形方块出现概率
  120. {
  121. {
  122. BLOCKNONE, BLOCKNONE, BLOCKRED, BLOCKNONE, BLOCKNONE,
  123. BLOCKNONE, BLOCKRED, BLOCKRED, BLOCKRED, BLOCKNONE,
  124. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  125. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  126. },
  127. {
  128. BLOCKNONE, BLOCKNONE, BLOCKRED, BLOCKNONE, BLOCKNONE,
  129. BLOCKNONE, BLOCKNONE, BLOCKRED, BLOCKRED, BLOCKNONE,
  130. BLOCKNONE, BLOCKNONE, BLOCKRED, BLOCKNONE, BLOCKNONE,
  131. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  132. },
  133. {
  134. BLOCKNONE, BLOCKRED, BLOCKRED, BLOCKRED, BLOCKNONE,
  135. BLOCKNONE, BLOCKNONE, BLOCKRED, BLOCKNONE, BLOCKNONE,
  136. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  137. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  138. },
  139. {
  140. BLOCKNONE, BLOCKNONE, BLOCKRED, BLOCKNONE, BLOCKNONE,
  141. BLOCKNONE, BLOCKRED, BLOCKRED, BLOCKNONE, BLOCKNONE,
  142. BLOCKNONE, BLOCKNONE, BLOCKRED, BLOCKNONE, BLOCKNONE,
  143. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  144. }
  145. },
  146. // '田'形方块
  147. {
  148. {
  149. BLOCKNONE, BLOCKNONE, BLOCKMAGENTA,BLOCKMAGENTA,BLOCKNONE,
  150. BLOCKNONE, BLOCKNONE, BLOCKMAGENTA,BLOCKMAGENTA,BLOCKNONE,
  151. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  152. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  153. },
  154. {
  155. BLOCKNONE, BLOCKNONE, BLOCKMAGENTA,BLOCKMAGENTA,BLOCKNONE,
  156. BLOCKNONE, BLOCKNONE, BLOCKMAGENTA,BLOCKMAGENTA,BLOCKNONE,
  157. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  158. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  159. },
  160. {
  161. BLOCKNONE, BLOCKNONE, BLOCKMAGENTA,BLOCKMAGENTA,BLOCKNONE,
  162. BLOCKNONE, BLOCKNONE, BLOCKMAGENTA,BLOCKMAGENTA,BLOCKNONE,
  163. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  164. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  165. },
  166. {
  167. BLOCKNONE, BLOCKNONE, BLOCKMAGENTA,BLOCKMAGENTA,BLOCKNONE,
  168. BLOCKNONE, BLOCKNONE, BLOCKMAGENTA,BLOCKMAGENTA,BLOCKNONE,
  169. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  170. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  171. }
  172. },
  173. // 'L'形方块
  174. {
  175. {
  176. BLOCKNONE, BLOCKNONE, BLOCKBROWN, BLOCKNONE, BLOCKNONE,
  177. BLOCKNONE, BLOCKNONE, BLOCKBROWN, BLOCKNONE, BLOCKNONE,
  178. BLOCKNONE, BLOCKNONE, BLOCKBROWN, BLOCKBROWN, BLOCKNONE,
  179. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  180. },
  181. {
  182. BLOCKNONE, BLOCKNONE, BLOCKBROWN, BLOCKBROWN, BLOCKBROWN,
  183. BLOCKNONE, BLOCKNONE, BLOCKBROWN, BLOCKNONE, BLOCKNONE,
  184. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  185. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  186. },
  187. {
  188. BLOCKNONE, BLOCKBROWN, BLOCKBROWN, BLOCKNONE, BLOCKNONE,
  189. BLOCKNONE, BLOCKNONE, BLOCKBROWN, BLOCKNONE, BLOCKNONE,
  190. BLOCKNONE, BLOCKNONE, BLOCKBROWN, BLOCKNONE, BLOCKNONE,
  191. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  192. },
  193. {
  194. BLOCKNONE, BLOCKNONE, BLOCKBROWN, BLOCKNONE, BLOCKNONE,
  195. BLOCKBROWN, BLOCKBROWN, BLOCKBROWN, BLOCKNONE, BLOCKNONE,
  196. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  197. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  198. }
  199. },
  200. //'L'形方块
  201. {
  202. {
  203. BLOCKNONE, BLOCKNONE, BLOCKYELLOW,BLOCKNONE, BLOCKNONE,
  204. BLOCKNONE, BLOCKNONE, BLOCKYELLOW,BLOCKNONE, BLOCKNONE,
  205. BLOCKNONE, BLOCKYELLOW,BLOCKYELLOW,BLOCKNONE, BLOCKNONE,
  206. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  207. },
  208. {
  209. BLOCKNONE, BLOCKNONE, BLOCKYELLOW,BLOCKNONE, BLOCKNONE,
  210. BLOCKNONE, BLOCKNONE, BLOCKYELLOW,BLOCKYELLOW,BLOCKYELLOW,
  211. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  212. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  213. },
  214. {
  215. BLOCKNONE, BLOCKNONE, BLOCKYELLOW,BLOCKYELLOW,BLOCKNONE,
  216. BLOCKNONE, BLOCKNONE, BLOCKYELLOW,BLOCKNONE, BLOCKNONE,
  217. BLOCKNONE, BLOCKNONE, BLOCKYELLOW,BLOCKNONE, BLOCKNONE,
  218. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  219. },
  220. {
  221. BLOCKNONE, BLOCKYELLOW,BLOCKYELLOW,BLOCKYELLOW,BLOCKNONE,
  222. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKYELLOW,BLOCKNONE,
  223. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  224. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  225. }
  226. }
  227. };
  228. // 游戏实时画面
  229. int tetrisMap[TETRISHEIGHT][TETRISWIDTH] =
  230. {
  231. };
  232. GAME_TETRIS player; // 玩家数据
  233. GAME_PREBLOCK preBlock; // 预生成方块数据
  234. GAME_MOVE_BLOCK moveBlock; // 当前控制方块数据
  235. int main(void)
  236. {
  237. tetrisrun();
  238. return 0;
  239. }
  240. void tetrisrun(void)
  241. {
  242. tetrisInit();
  243. while(1)
  244. {
  245. player.ctime = GetTickCount();
  246. if( (player.ctime - 500 + 30*player.level) > player.ttime )
  247. {
  248. player.moveFlag = TRUE;
  249. player.ttime = GetTickCount();
  250. }
  251. if(player.moveFlag)
  252. {
  253. player.moveFlag = FALSE;
  254. tetrisMoveDown();
  255. tetrisDraw();
  256. }
  257. if(_kbhit())
  258. {
  259. player.key = _getch();
  260. tetrisKeyHandle();
  261. }
  262. if(player.newBlockFlag)
  263. {
  264. player.newBlockFlag = FALSE;
  265. tetrisLoadBlock();
  266. tetrisRemove();
  267. tetrisNewBlock();
  268. tetrisIsOver();
  269. }
  270. if(player.gameOverFlag)
  271. {
  272. player.gameOverFlag = FALSE;
  273. break;
  274. }
  275. }
  276. }
  277. void tetrisInit(void)
  278. {
  279. int i,j,k;
  280. HWND window = initgraph(510, 620);
  281. SetWindowText(window, "俄罗斯方块 - by耒阳阿杰");
  282. //SetWindowPos(window,HWND_TOPMOST,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE);
  283. rectangle(GAMEMAP1 - 2,GAMEMAP2 - 2,GAMEMAP3 + 2,GAMEMAP4 + 2);
  284. rectangle(GAMEMAP5 - 2,GAMEMAP6 - 2,GAMEMAP7 + 2,GAMEMAP8 + 2);
  285. rectangle(GAMEGUIDE1 - 5,GAMEGUIDE2 - 5,GAMEGUIDE3 + 5,GAMEGUIDE4 + 5);
  286. outtextxy(GAMEMAP5 + 10 ,GAMEMAP6 - 26,"下个方块");
  287. settextcolor(GREEN);
  288. outtextxy(GAMEGUIDE1 ,GAMEGUIDE2 ,"W :改变形状");
  289. outtextxy(GAMEGUIDE1 ,GAMEGUIDE2 + 25 ,"A :方块左移");
  290. outtextxy(GAMEGUIDE1 ,GAMEGUIDE2 + 50 ,"S :方块右移");
  291. outtextxy(GAMEGUIDE1 ,GAMEGUIDE2 + 75 ,"D :方块下移");
  292. outtextxy(GAMEGUIDE1 ,GAMEGUIDE2 + 100 ,"R :重新开始");
  293. outtextxy(GAMEGUIDE1 ,GAMEGUIDE2 + 125 ,"ESC:退出游戏");
  294. memset ( &player, 0, sizeof ( GAME_TETRIS ) );
  295. memset ( &preBlock, 0, sizeof ( GAME_PREBLOCK ) );
  296. memset(tetrisMap,0,sizeof(tetrisMap));
  297. player.ttime = GetTickCount();
  298. srand((unsigned)time(NULL));
  299. i = (rand() + 1) % BLOCKTYPE;
  300. j = rand() % BLOCKSTYLE;
  301. for(k = 0; k < BLOCKSPACE;k++)
  302. {
  303. preBlock.tetrisPreBlock[k] = block[i][j][k];
  304. }
  305. preBlock.blockType = i;
  306. preBlock.blockStyle = j;
  307. tetrisNewBlock();
  308. }
  309. void tetrisDraw(void)
  310. {
  311. int i,j;
  312. int x,y;
  313. char ch[20];
  314. settextstyle(20,12,"宋体");
  315. settextcolor(CYAN);
  316. if(player.level < 10)
  317. {
  318. sprintf_s(ch, "%s%d","LEVEL:", player.level);
  319. }
  320. else
  321. {
  322. sprintf_s(ch, "%s","LEVEL:MAX");
  323. }
  324. outtextxy(GAMELEVEL1,GAMELEVEL2,ch);
  325. settextcolor(LIGHTBLUE);
  326. sprintf_s(ch, "%s%d","RANK:", player.rank);
  327. outtextxy(GAMERANK1,GAMERANK2,ch);
  328. setfillcolor(BLACK);
  329. solidrectangle(GAMEMAP5,GAMEMAP6,GAMEMAP7,GAMEMAP8);
  330. for(i = 0; i < BLOCKSPACE; i++)
  331. {
  332. switch(preBlock.tetrisPreBlock[i])
  333. {
  334. case BLOCKNONE:
  335. continue;
  336. break;
  337. case BLOCKBLUE:
  338. setfillcolor(BLUE);
  339. break;
  340. case BLOCKGREEN:
  341. setfillcolor(GREEN);
  342. break;
  343. case BLOCKCYAN:
  344. setfillcolor(CYAN);
  345. break;
  346. case BLOCKRED:
  347. setfillcolor(RED);
  348. break;
  349. case BLOCKMAGENTA:
  350. setfillcolor(MAGENTA);
  351. break;
  352. case BLOCKBROWN:
  353. setfillcolor(BROWN);
  354. break;
  355. case BLOCKYELLOW:
  356. setfillcolor(YELLOW);
  357. break;
  358. }
  359. fillrectangle(GAMEMAP5 + (i%5) * SIDELENGTH ,GAMEMAP6 + (i/5) * SIDELENGTH ,GAMEMAP5 + ((i%5)+1)*SIDELENGTH ,GAMEMAP6 + ((i/5)+1) * SIDELENGTH );
  360. }
  361. setfillcolor(BLACK);
  362. solidrectangle(GAMEMAP1,GAMEMAP2,GAMEMAP3,GAMEMAP4);
  363. for(i = 0; i < 4; i++)
  364. {
  365. x = moveBlock.blockSite[i][0];
  366. y = moveBlock.blockSite[i][1];
  367. switch(moveBlock.blockColor)
  368. {
  369. case BLOCKNONE:
  370. continue;
  371. break;
  372. case BLOCKBLUE:
  373. setfillcolor(BLUE);
  374. break;
  375. case BLOCKGREEN:
  376. setfillcolor(GREEN);
  377. break;
  378. case BLOCKCYAN:
  379. setfillcolor(CYAN);
  380. break;
  381. case BLOCKRED:
  382. setfillcolor(RED);
  383. break;
  384. case BLOCKMAGENTA:
  385. setfillcolor(MAGENTA);
  386. break;
  387. case BLOCKBROWN:
  388. setfillcolor(BROWN);
  389. break;
  390. case BLOCKYELLOW:
  391. setfillcolor(YELLOW);
  392. break;
  393. }
  394. fillrectangle(GAMEMAP1 + y * SIDELENGTH ,GAMEMAP2 + x * SIDELENGTH ,GAMEMAP1 + (y+1)*SIDELENGTH ,GAMEMAP2 + (x+1) * SIDELENGTH );
  395. }
  396. for(i = 0; i < TETRISHEIGHT; i++)
  397. {
  398. for(j = 0; j < TETRISWIDTH; j++)
  399. {
  400. switch(tetrisMap[i][j])
  401. {
  402. case BLOCKNONE:
  403. continue;
  404. break;
  405. case BLOCKBLUE:
  406. setfillcolor(BLUE);
  407. break;
  408. case BLOCKGREEN:
  409. setfillcolor(GREEN);
  410. break;
  411. case BLOCKCYAN:
  412. setfillcolor(CYAN);
  413. break;
  414. case BLOCKRED:
  415. setfillcolor(RED);
  416. break;
  417. case BLOCKMAGENTA:
  418. setfillcolor(MAGENTA);
  419. break;
  420. case BLOCKBROWN:
  421. setfillcolor(BROWN);
  422. break;
  423. case BLOCKYELLOW:
  424. setfillcolor(YELLOW);
  425. break;
  426. }
  427. fillrectangle(GAMEMAP1 + j * SIDELENGTH ,GAMEMAP2 + i * SIDELENGTH ,GAMEMAP1 + (j+1)*SIDELENGTH ,GAMEMAP2 + (i+1) * SIDELENGTH );
  428. }
  429. }
  430. }
  431. void tetrisNewBlock(void)
  432. {
  433. int i,j,k;
  434. k = 0;
  435. for(i = 0; i < BLOCKSPACE; i++)
  436. {
  437. if(preBlock.tetrisPreBlock[i])
  438. {
  439. moveBlock.blockColor = preBlock.tetrisPreBlock[i];
  440. moveBlock.blockSite[k][0] = i/5;
  441. moveBlock.blockSite[k][1] = 5 + i%5;
  442. k++;
  443. }
  444. }
  445. player.blockType = preBlock.blockType;
  446. player.blockStyle = preBlock.blockStyle;
  447. srand((unsigned)time(NULL));
  448. i = rand() % BLOCKTYPE;
  449. j = rand() % BLOCKSTYLE;
  450. for(k = 0; k < BLOCKSPACE;k++)
  451. {
  452. preBlock.tetrisPreBlock[k] = block[i][j][k];
  453. }
  454. preBlock.blockType = i;
  455. preBlock.blockStyle = j;
  456. }
  457. void tetrisMoveUp(void)
  458. {
  459. int i,k;
  460. int bStyle;
  461. int ux,uy; // 改变方块朝向前后,方块格的位移矢量
  462. int blocksite2[4][2]; // 记录改变朝向前,4个方块的方块库坐标
  463. int blocksite3[4][2]; // 记录改变朝向后,4个方块的方块库坐标
  464. int blocksite4[4][2]; // 记录改变朝向后,4个方块的游戏地图坐标
  465. k = 0;
  466. for(i = 0; i < BLOCKSPACE; i++)
  467. {
  468. if(k > 3)
  469. {
  470. break;
  471. }
  472. if(block[player.blockType][player.blockStyle][i])
  473. {
  474. blocksite2[k][0] = i / 5;
  475. blocksite2[k][1] = i % 5;
  476. k++;
  477. }
  478. }
  479. k = 0;
  480. bStyle = (player.blockStyle + 1)%BLOCKSTYLE;
  481. for(i = 0; i < BLOCKSPACE; i++)
  482. {
  483. if(k > 3)
  484. {
  485. break;
  486. }
  487. if(block[player.blockType][bStyle][i])
  488. {
  489. blocksite3[k][0] = i / 5;
  490. blocksite3[k][1] = i % 5;
  491. k++;
  492. }
  493. }
  494. for(i = 0; i < 4; i++)
  495. {
  496. ux = blocksite3[i][0] - blocksite2[i][0];
  497. uy = blocksite3[i][1] - blocksite2[i][1];
  498. blocksite4[i][0] = moveBlock.blockSite[i][0] + ux;
  499. blocksite4[i][1] = moveBlock.blockSite[i][1] + uy;
  500. if( (blocksite4[i][0] < 0)
  501. || (blocksite4[i][1] < 0)
  502. || (blocksite4[i][0] > TETRISHEIGHT - 1)
  503. || (blocksite4[i][1] > TETRISWIDTH - 1)
  504. || ( tetrisMap[blocksite4[i][0]][blocksite4[i][1]] ) )
  505. {
  506. return;
  507. }
  508. }
  509. for(i = 0;i < 4; i++)
  510. {
  511. moveBlock.blockSite[i][0] = blocksite4[i][0];
  512. moveBlock.blockSite[i][1] = blocksite4[i][1];
  513. }
  514. player.blockStyle = bStyle;
  515. }
  516. void tetrisMoveDown(void)
  517. {
  518. int i;
  519. int x,y;
  520. for(i = 0; i < 4; i++)
  521. {
  522. if(moveBlock.blockSite[i][0] > TETRISHEIGHT - 2)
  523. {
  524. player.newBlockFlag = TRUE;
  525. return;
  526. }
  527. }
  528. for(i = 0; i < 4; i++)
  529. {
  530. x = moveBlock.blockSite[i][0] + 1;
  531. y = moveBlock.blockSite[i][1];
  532. if(tetrisMap[x][y])
  533. {
  534. player.newBlockFlag = TRUE;
  535. return;
  536. }
  537. }
  538. for(i = 0; i < 4; i++)
  539. {
  540. moveBlock.blockSite[i][0]++;
  541. }
  542. }
  543. void tetrisMoveLeft(void)
  544. {
  545. int i;
  546. int x,y;
  547. for(i = 0; i < 4; i++)
  548. {
  549. x = moveBlock.blockSite[i][0];
  550. y = moveBlock.blockSite[i][1];
  551. if( (y - 1) < 0
  552. || tetrisMap[x][y - 1])
  553. {
  554. return;
  555. }
  556. }
  557. for(i = 0; i < 4; i++)
  558. {
  559. moveBlock.blockSite[i][1]--;
  560. }
  561. }
  562. void tetrisMoveRight(void)
  563. {
  564. int i;
  565. int x,y;
  566. //k = 0;
  567. for(i = 0; i < 4; i++)
  568. {
  569. x = moveBlock.blockSite[i][0];
  570. y = moveBlock.blockSite[i][1];
  571. if( ((y + 1) >= TETRISWIDTH)
  572. || (tetrisMap[x][y + 1]) )
  573. {
  574. return;
  575. }
  576. }
  577. for(i = 0; i < 4; i++)
  578. {
  579. moveBlock.blockSite[i][1]++;
  580. }
  581. }
  582. void tetrisLoadBlock(void)
  583. {
  584. int i;
  585. int x,y;
  586. for(i = 0; i < 4; i++)
  587. {
  588. x = moveBlock.blockSite[i][0];
  589. y = moveBlock.blockSite[i][1];
  590. if(!tetrisMap[x][y])
  591. {
  592. tetrisMap[x][y] = moveBlock.blockColor;
  593. }
  594. }
  595. }
  596. void tetrisIsOver(void)
  597. {
  598. int i;
  599. int x,y;
  600. for(i = 0; i < 4; i++)
  601. {
  602. x = moveBlock.blockSite[i][0];
  603. y = moveBlock.blockSite[i][1];
  604. if(tetrisMap[x][y])
  605. {
  606. player.gameOverFlag = TRUE;
  607. tetrisDraw();
  608. tetrisQuit();
  609. return;
  610. }
  611. }
  612. }
  613. void tetrisQuit(void)
  614. {
  615. int key,flag;
  616. flag = player.gameOverFlag;
  617. key = MessageBox(NULL,"是否退出游戏?","提示",MB_YESNO| MB_SYSTEMMODAL);
  618. switch(key)
  619. {
  620. case IDYES:
  621. player.gameOverFlag = TRUE;
  622. break;
  623. case IDNO:
  624. player.gameOverFlag = FALSE;
  625. break;
  626. default:
  627. break;
  628. }
  629. while(flag
  630. && !player.gameOverFlag)
  631. {
  632. key = MessageBox(NULL,"检测到游戏已无法继续,\n请选择\"是\": 退出游戏;\n或者选择\"否\": 重启游戏;","提示",MB_YESNO| MB_SYSTEMMODAL);
  633. switch(key)
  634. {
  635. case IDYES:
  636. player.gameOverFlag = TRUE;
  637. break;
  638. case IDNO:
  639. tetrisInit();
  640. player.gameOverFlag = FALSE;
  641. return;
  642. default:
  643. break;
  644. }
  645. }
  646. CLEARKEY();
  647. }
  648. void tetrisKeyHandle(void)
  649. {
  650. switch(player.key)
  651. {
  652. case 'w':
  653. case 'W':
  654. tetrisMoveUp();
  655. break;
  656. case 'a':
  657. case 'A':
  658. tetrisMoveLeft();
  659. break;
  660. case 's':
  661. case 'S':
  662. player.moveFlag = TRUE;
  663. player.ttime = GetTickCount();
  664. return;
  665. case 'd':
  666. case 'D':
  667. tetrisMoveRight();
  668. break;
  669. case ESC:
  670. tetrisQuit();
  671. break;
  672. case 'r':
  673. case 'R':
  674. tetrisReset();
  675. break;
  676. default:
  677. break;
  678. }
  679. tetrisDraw();
  680. }
  681. void tetrisReset(void)
  682. {
  683. int key;
  684. key = MessageBox(NULL,"是否重新开始游戏?","提示",MB_YESNO| MB_SYSTEMMODAL);
  685. CLEARKEY();
  686. switch(key)
  687. {
  688. case IDYES:
  689. tetrisInit();
  690. break;
  691. case IDNO:
  692. break;
  693. default:
  694. break;
  695. }
  696. }
  697. void tetrisRemove(void)
  698. {
  699. int i,j,m,n;
  700. int flag,bnum;
  701. bnum = 0;
  702. for(i = TETRISHEIGHT - 1; i >= 0; i-- )
  703. {
  704. flag = 0;
  705. for(j = 0; j < TETRISWIDTH; j++)
  706. {
  707. if(!tetrisMap[i][j])
  708. {
  709. flag = 1;
  710. break;
  711. }
  712. }
  713. if(flag)
  714. {
  715. continue;
  716. }
  717. bnum++;
  718. for(j = 0; j < TETRISWIDTH; j++)
  719. {
  720. tetrisMap[i][j] = BLOCKNONE;
  721. }
  722. for(m = i; m > 0; m--)
  723. {
  724. for(n = 0; n < TETRISWIDTH; n++)
  725. {
  726. tetrisMap[m][n] = tetrisMap[m - 1][n];
  727. }
  728. }
  729. i++;
  730. }
  731. if(!bnum)
  732. {
  733. return;
  734. }
  735. switch(bnum)
  736. {
  737. case 0:
  738. break;
  739. case 1:
  740. player.rank += 10;
  741. break;
  742. case 2:
  743. player.rank += 20;
  744. break;
  745. case 3:
  746. player.rank += 40;
  747. break;
  748. case 4:
  749. player.rank += 80;
  750. break;
  751. default:
  752. break;
  753. }
  754. player.level = player.rank/100;
  755. if(player.level > 10)
  756. {
  757. player.level = 10;
  758. }
  759. }

二、easyX库安装

EasyX Graphics Library for C++

进入以上链接,下载并安装easyX库

三、宏定义、变量的说明

  • 1.方块像素

方块为正方形,大小为20X20像素

#define		SIDELENGTH		20
  • 2.游戏地图区域

游戏地图为矩形,大小为600X300像素,四个角坐标为下面的宏定义。

  1. #define GAMEMAP1 200
  2. #define GAMEMAP2 10
  3. #define GAMEMAP3 500
  4. #define GAMEMAP4 610

 使用rectangle函数,填入四个角坐标,画出游戏地图边界

rectangle(GAMEMAP1 - 2,GAMEMAP2 - 2,GAMEMAP3 + 2,GAMEMAP4 + 2);

 

把地图按20X20像素分割,就是30X15个方块,这样就把地图转换为坐标的形式了,选中一个方块格,再进行涂色,就是一个相应的方块了。

  • 3.预生成方块区域

预生成方块区域为100X80像素的矩形,四个角坐标如下宏定义

  1. #define GAMEMAP5 40 // 预生成方块区域
  2. #define GAMEMAP6 50
  3. #define GAMEMAP7 140
  4. #define GAMEMAP8 130

使用rectangle函数,填入四个角坐标,画出游戏地图边界

rectangle(GAMEMAP5 - 2,GAMEMAP6 - 2,GAMEMAP7 + 2,GAMEMAP8 + 2);	

把区域按照20X20像素分割,就是4X5个方块,这样就可以转换为坐标的形式;

根据随机得到的方块种类,换算成四个坐标,再选择颜色进行填充,就可以得到预生成的方块了。

  • 4.玩家数据结构

  1. typedef struct _GAME_TETRIS
  2. {
  3. int ttime; // 计时
  4. int ctime; // 计时
  5. int level; // 当前游戏等级
  6. int rank; // 当前游戏分数
  7. int key; // 玩家按键值
  8. int blockType; // 当前方块类型
  9. int blockStyle; // 当前方块朝向
  10. bool moveFlag; // 移动标识,标识为1时,移动当前方块
  11. bool newBlockFlag; // 载入新方块标识,当标识为真时,将预生成的随机方块载入地图
  12. bool gameOverFlag; // 游戏结束标识
  13. }GAME_TETRIS;

GAME_TETRIS        player;        // 玩家数据

player会保存玩家在游戏中的数据,ttime和ctime是用来定时的,每隔(500ms - 等级*30毫秒),会将moveflag置1,方块下降一行;

level和rank是玩家当前获得分数,已经根据分数得到的level;

key是玩家按下的键盘输入值;

blockType和blockStyle是玩家当前控制方块的方块类型和方块朝向,用来判定方块是否可以左、右、下移、变换形态;

moveflag,移动标识,标识为1时,当前方块会下移一行;

bewBlockFlag,新方块标识,标识为1时,会执行以下函数:当前方块载入游戏地图、消除满行的方块、生成新的方块、判断游戏是否结束;

gameOverFlag,游戏结束标识,标识为1时,会让玩家选择结束游戏,或重新开始游戏

  • 5.所有方块数据库

数据库是一个三维数组

总共有8种类型的方块,每个类型的方块颜色都不一样;每个方块有4个样式(朝向);每种方块的每种朝向都是一个5X4的方块矩形。

生成新方块时,就会从方块数据库中获取方块数据。

  1. #define BLOCKTYPE 8 // 方块种类数
  2. #define BLOCKSTYLE 4 // 每种方块的样式数
  3. #define BLOCKSPACE 20 // 方块占用空间 5 x 4
  1. const int block[BLOCKTYPE][BLOCKSTYLE][BLOCKSPACE] =
  2. {
  3. // 总共有8种方块,每种方块有4种样式
  4. // 'I'形方块
  5. {
  6. {
  7. BLOCKNONE, BLOCKBLUE, BLOCKBLUE, BLOCKBLUE, BLOCKBLUE,
  8. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  9. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  10. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  11. },
  12. {
  13. BLOCKNONE, BLOCKNONE, BLOCKBLUE, BLOCKNONE, BLOCKNONE,
  14. BLOCKNONE, BLOCKNONE, BLOCKBLUE, BLOCKNONE, BLOCKNONE,
  15. BLOCKNONE, BLOCKNONE, BLOCKBLUE, BLOCKNONE, BLOCKNONE,
  16. BLOCKNONE, BLOCKNONE, BLOCKBLUE, BLOCKNONE, BLOCKNONE
  17. },
  18. {
  19. BLOCKBLUE, BLOCKBLUE, BLOCKBLUE, BLOCKBLUE, BLOCKNONE,
  20. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  21. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  22. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  23. },
  24. {
  25. BLOCKNONE, BLOCKNONE, BLOCKBLUE, BLOCKNONE, BLOCKNONE,
  26. BLOCKNONE, BLOCKNONE, BLOCKBLUE, BLOCKNONE, BLOCKNONE,
  27. BLOCKNONE, BLOCKNONE, BLOCKBLUE, BLOCKNONE, BLOCKNONE,
  28. BLOCKNONE, BLOCKNONE, BLOCKBLUE, BLOCKNONE, BLOCKNONE
  29. }
  30. },
  31. // 'Z'形方块
  32. {
  33. {
  34. BLOCKNONE, BLOCKGREEN, BLOCKGREEN, BLOCKNONE, BLOCKNONE,
  35. BLOCKNONE, BLOCKNONE, BLOCKGREEN, BLOCKGREEN, BLOCKNONE,
  36. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  37. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  38. },
  39. {
  40. BLOCKNONE, BLOCKNONE, BLOCKGREEN, BLOCKNONE, BLOCKNONE,
  41. BLOCKNONE, BLOCKGREEN, BLOCKGREEN, BLOCKNONE, BLOCKNONE,
  42. BLOCKNONE, BLOCKGREEN, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  43. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  44. },
  45. {
  46. BLOCKNONE, BLOCKGREEN, BLOCKGREEN, BLOCKNONE, BLOCKNONE,
  47. BLOCKNONE, BLOCKNONE, BLOCKGREEN, BLOCKGREEN, BLOCKNONE,
  48. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  49. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  50. },
  51. {
  52. BLOCKNONE, BLOCKNONE, BLOCKGREEN, BLOCKNONE, BLOCKNONE,
  53. BLOCKNONE, BLOCKGREEN, BLOCKGREEN, BLOCKNONE, BLOCKNONE,
  54. BLOCKNONE, BLOCKGREEN, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  55. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  56. }
  57. },
  58. //'Z'形方块
  59. {
  60. {
  61. BLOCKNONE, BLOCKNONE, BLOCKCYAN, BLOCKCYAN, BLOCKNONE,
  62. BLOCKNONE, BLOCKCYAN, BLOCKCYAN, BLOCKNONE, BLOCKNONE,
  63. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  64. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  65. },
  66. {
  67. BLOCKNONE, BLOCKNONE, BLOCKCYAN, BLOCKNONE, BLOCKNONE,
  68. BLOCKNONE, BLOCKNONE, BLOCKCYAN, BLOCKCYAN, BLOCKNONE,
  69. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKCYAN, BLOCKNONE,
  70. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  71. },
  72. {
  73. BLOCKNONE, BLOCKNONE, BLOCKCYAN, BLOCKCYAN, BLOCKNONE,
  74. BLOCKNONE, BLOCKCYAN, BLOCKCYAN, BLOCKNONE, BLOCKNONE,
  75. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  76. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  77. },
  78. {
  79. BLOCKNONE, BLOCKNONE, BLOCKCYAN, BLOCKNONE, BLOCKNONE,
  80. BLOCKNONE, BLOCKNONE, BLOCKCYAN, BLOCKCYAN, BLOCKNONE,
  81. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKCYAN, BLOCKNONE,
  82. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  83. }
  84. },
  85. // 'T'形方块
  86. {
  87. {
  88. BLOCKNONE, BLOCKNONE, BLOCKRED, BLOCKNONE, BLOCKNONE,
  89. BLOCKNONE, BLOCKRED, BLOCKRED, BLOCKRED, BLOCKNONE,
  90. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  91. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  92. },
  93. {
  94. BLOCKNONE, BLOCKNONE, BLOCKRED, BLOCKNONE, BLOCKNONE,
  95. BLOCKNONE, BLOCKNONE, BLOCKRED, BLOCKRED, BLOCKNONE,
  96. BLOCKNONE, BLOCKNONE, BLOCKRED, BLOCKNONE, BLOCKNONE,
  97. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  98. },
  99. {
  100. BLOCKNONE, BLOCKRED, BLOCKRED, BLOCKRED, BLOCKNONE,
  101. BLOCKNONE, BLOCKNONE, BLOCKRED, BLOCKNONE, BLOCKNONE,
  102. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  103. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  104. },
  105. {
  106. BLOCKNONE, BLOCKNONE, BLOCKRED, BLOCKNONE, BLOCKNONE,
  107. BLOCKNONE, BLOCKRED, BLOCKRED, BLOCKNONE, BLOCKNONE,
  108. BLOCKNONE, BLOCKNONE, BLOCKRED, BLOCKNONE, BLOCKNONE,
  109. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  110. }
  111. },
  112. // 'T'形方块 X 2 增加T形方块出现概率
  113. {
  114. {
  115. BLOCKNONE, BLOCKNONE, BLOCKRED, BLOCKNONE, BLOCKNONE,
  116. BLOCKNONE, BLOCKRED, BLOCKRED, BLOCKRED, BLOCKNONE,
  117. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  118. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  119. },
  120. {
  121. BLOCKNONE, BLOCKNONE, BLOCKRED, BLOCKNONE, BLOCKNONE,
  122. BLOCKNONE, BLOCKNONE, BLOCKRED, BLOCKRED, BLOCKNONE,
  123. BLOCKNONE, BLOCKNONE, BLOCKRED, BLOCKNONE, BLOCKNONE,
  124. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  125. },
  126. {
  127. BLOCKNONE, BLOCKRED, BLOCKRED, BLOCKRED, BLOCKNONE,
  128. BLOCKNONE, BLOCKNONE, BLOCKRED, BLOCKNONE, BLOCKNONE,
  129. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  130. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  131. },
  132. {
  133. BLOCKNONE, BLOCKNONE, BLOCKRED, BLOCKNONE, BLOCKNONE,
  134. BLOCKNONE, BLOCKRED, BLOCKRED, BLOCKNONE, BLOCKNONE,
  135. BLOCKNONE, BLOCKNONE, BLOCKRED, BLOCKNONE, BLOCKNONE,
  136. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  137. }
  138. },
  139. // '田'形方块
  140. {
  141. {
  142. BLOCKNONE, BLOCKNONE, BLOCKMAGENTA,BLOCKMAGENTA,BLOCKNONE,
  143. BLOCKNONE, BLOCKNONE, BLOCKMAGENTA,BLOCKMAGENTA,BLOCKNONE,
  144. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  145. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  146. },
  147. {
  148. BLOCKNONE, BLOCKNONE, BLOCKMAGENTA,BLOCKMAGENTA,BLOCKNONE,
  149. BLOCKNONE, BLOCKNONE, BLOCKMAGENTA,BLOCKMAGENTA,BLOCKNONE,
  150. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  151. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  152. },
  153. {
  154. BLOCKNONE, BLOCKNONE, BLOCKMAGENTA,BLOCKMAGENTA,BLOCKNONE,
  155. BLOCKNONE, BLOCKNONE, BLOCKMAGENTA,BLOCKMAGENTA,BLOCKNONE,
  156. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  157. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  158. },
  159. {
  160. BLOCKNONE, BLOCKNONE, BLOCKMAGENTA,BLOCKMAGENTA,BLOCKNONE,
  161. BLOCKNONE, BLOCKNONE, BLOCKMAGENTA,BLOCKMAGENTA,BLOCKNONE,
  162. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  163. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  164. }
  165. },
  166. // 'L'形方块
  167. {
  168. {
  169. BLOCKNONE, BLOCKNONE, BLOCKBROWN, BLOCKNONE, BLOCKNONE,
  170. BLOCKNONE, BLOCKNONE, BLOCKBROWN, BLOCKNONE, BLOCKNONE,
  171. BLOCKNONE, BLOCKNONE, BLOCKBROWN, BLOCKBROWN, BLOCKNONE,
  172. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  173. },
  174. {
  175. BLOCKNONE, BLOCKNONE, BLOCKBROWN, BLOCKBROWN, BLOCKBROWN,
  176. BLOCKNONE, BLOCKNONE, BLOCKBROWN, BLOCKNONE, BLOCKNONE,
  177. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  178. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  179. },
  180. {
  181. BLOCKNONE, BLOCKBROWN, BLOCKBROWN, BLOCKNONE, BLOCKNONE,
  182. BLOCKNONE, BLOCKNONE, BLOCKBROWN, BLOCKNONE, BLOCKNONE,
  183. BLOCKNONE, BLOCKNONE, BLOCKBROWN, BLOCKNONE, BLOCKNONE,
  184. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  185. },
  186. {
  187. BLOCKNONE, BLOCKNONE, BLOCKBROWN, BLOCKNONE, BLOCKNONE,
  188. BLOCKBROWN, BLOCKBROWN, BLOCKBROWN, BLOCKNONE, BLOCKNONE,
  189. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  190. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  191. }
  192. },
  193. //'L'形方块
  194. {
  195. {
  196. BLOCKNONE, BLOCKNONE, BLOCKYELLOW,BLOCKNONE, BLOCKNONE,
  197. BLOCKNONE, BLOCKNONE, BLOCKYELLOW,BLOCKNONE, BLOCKNONE,
  198. BLOCKNONE, BLOCKYELLOW,BLOCKYELLOW,BLOCKNONE, BLOCKNONE,
  199. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  200. },
  201. {
  202. BLOCKNONE, BLOCKNONE, BLOCKYELLOW,BLOCKNONE, BLOCKNONE,
  203. BLOCKNONE, BLOCKNONE, BLOCKYELLOW,BLOCKYELLOW,BLOCKYELLOW,
  204. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  205. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  206. },
  207. {
  208. BLOCKNONE, BLOCKNONE, BLOCKYELLOW,BLOCKYELLOW,BLOCKNONE,
  209. BLOCKNONE, BLOCKNONE, BLOCKYELLOW,BLOCKNONE, BLOCKNONE,
  210. BLOCKNONE, BLOCKNONE, BLOCKYELLOW,BLOCKNONE, BLOCKNONE,
  211. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  212. },
  213. {
  214. BLOCKNONE, BLOCKYELLOW,BLOCKYELLOW,BLOCKYELLOW,BLOCKNONE,
  215. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKYELLOW,BLOCKNONE,
  216. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE,
  217. BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE, BLOCKNONE
  218. }
  219. }
  220. };
  • 6.当前控制方块数据结构

  1. typedef struct _GAME_MOVE_BLOCK
  2. {
  3. int blockSite[4][2]; // 4个方块格坐标
  4. int blockColor; // 方块颜色
  5. }GAME_MOVE_BLOCK;

GAME_MOVE_BLOCK moveBlock;    // 当前控制方块数据

数据结构由两部分组成,一个是保存了四个方块坐标的二维数组;一个是当前控制方块的颜色;

有了坐标和颜色,就可以在游戏地图中,准确的画出当前控制方块。

四、主函数tetrisrun()

  • 1.函数说明

主函数由一个初始化函数,和一个死循环组成;初始化游戏数据后,会在死循环while(1)中不停的执行游戏数据处理,直到玩家选择退出游戏。

通过主函数,也能看出来整个代码的运行逻辑,了解编程思路。

  1. void tetrisrun(void)
  2. {
  3. tetrisInit();
  4. while(1)
  5. {
  6. // (每500毫秒 - 游戏等级*30毫秒)运行一次,方块下移
  7. player.ctime = GetTickCount();
  8. if( (player.ctime - 500 + 30*player.level) > player.ttime )
  9. {
  10. player.moveFlag = TRUE;
  11. player.ttime = GetTickCount();
  12. }
  13. // 如果移动标识为1,则当前方块下移
  14. if(player.moveFlag)
  15. {
  16. player.moveFlag = FALSE;
  17. tetrisMoveDown();
  18. tetrisDraw();
  19. }
  20. // 用户按键处理
  21. if(_kbhit())
  22. {
  23. player.key = _getch();
  24. tetrisKeyHandle();
  25. }
  26. // 将预生成的随机方块载入游戏地图
  27. if(player.newBlockFlag)
  28. {
  29. player.newBlockFlag = FALSE;
  30. // 将当前方块载入游戏地图
  31. tetrisLoadBlock();
  32. // 消除满行的方块
  33. tetrisRemove();
  34. // 生成新的方块
  35. tetrisNewBlock();
  36. // 判断游戏是否结束
  37. tetrisIsOver();
  38. }
  39. // 游戏结束处理
  40. if(player.gameOverFlag)
  41. {
  42. player.gameOverFlag = FALSE;
  43. break;
  44. }
  45. }
  46. }
  •  2.计时部分

// (每500毫秒 - 游戏等级*30毫秒)运行一次,方块下移
        player.ctime = GetTickCount();
        if( (player.ctime - 500 + 30*player.level) > player.ttime )
        {    
            player.moveFlag = TRUE;
            player.ttime = GetTickCount();
        }

第一部分,计时部分:通过记录两个时间变量的时间差,来得到一个周期执行的标识

1.初始化时,先给变量player.ttime赋值,获得当时的时间数据;

2.每次主程序循环时,都会给另一个变量player.ctime赋值,获得最新的时间数据;

3.以默认的500ms周期为例,如果最新的时间player.ctime减去500ms,还是大于当时的时间player.ttime,说明离第一次给player.ttime赋值,已经过去了500ms,满足一个周期;

4.此时,给周期执行标识 player.moveFlag置1;表示当前方块需要下移了,将会在后面的函数处理,并将标识置0,等待下一次周期来临;

5.再给player.ttime赋值,获得当时的时间数据,开始执行下一次500ms的周期计算。

  • 3.方块移动

// 如果移动标识为1,则当前方块下移
        if(player.moveFlag)
        {
            player.moveFlag = FALSE;

            tetrisMoveDown();
            tetrisDraw();
        }

第二部分,方块移动:每过一个周期,方块移动标识player.moveFlag置1,执行这部分函数

1.先将移动标识player.moveFlag置0,方便下一次周期的执行;

2.执行方块下移函数tetrisMoveDown(),对方块进行下移处理;如果方块到达游戏底部,或者下方已经存在方块,就会将生成新方块标识player.newBlockFlag置1,进行后面的处理;

3.执行绘制地图函数tetrisDraw(),画出方块下移后的画面。

  • 4.按键处理

// 用户按键处理
        if(_kbhit())
        {    
            player.key = _getch();
            tetrisKeyHandle();
        }

第三部分,按键处理:对用户的按键进行处理,控制方块的移动、游戏的进程

1._kbhit()函数,每当检测到用户按下按键时,会返回TRUE,此时if条件为真,执行if里面的代码;

2.通过_getch()函数,将用户按键值赋给变量player.key;

3.执行按键处理函数tetrisKeyHandle(),对不同的按键进行不同的处理

  • 5.新方块处理

// 将预生成的随机方块载入游戏地图
        if(player.newBlockFlag)
        {
            player.newBlockFlag = FALSE;
            // 将当前方块载入游戏地图
            tetrisLoadBlock();
            // 消除满行的方块
            tetrisRemove();
            // 生成新的方块
            tetrisNewBlock();
            // 判断游戏是否结束
            tetrisIsOver();
        }

第四部分,新方块处理:每当当前方块到达游戏底部,或者当前方块下一行已存在方块时,会将新方块标识player.newBlockFlag置1,执行if里面的代码。

1.先把新方块标识player.newBlockFlag清0,防止重复执行;

2.当前方块已不能下移,执行tetrisLoadBlock()函数,把当前方块固定到游戏地图,不再移动;

3.固定方块后,执行tetrisRemove()函数,检测并消除满行的方块,并通过消除的行数,增加RANK分,改变level等级;

4.消除满行后,执行tetrisNewBlock()函数,将预生成的方块载入游戏,成为当前控制方块,并生成新的随机方块,载入到预生成方块地图;

5.生成新方块后,执行 tetrisIsOver()函数,判断当前控制方块是否和游戏地图有重叠,如果重叠,则游戏结束,将结束标识player.gameOverFlag置1,并让用户选择是结束游戏还是重新开始。

  • 6.游戏结束

// 游戏结束处理
        if(player.gameOverFlag)
        {
            player.gameOverFlag = FALSE;
            break;
        }

第五部分,游戏结束处理:如果用户决定结束游戏, 则退出while(1)死循环,退出程序。

五、代码和所有函数说明

上面已经解释过主函数tetrisrun(),接下来会详细解释每一个函数

  • 1. tetrisInit()

初始化函数,程序会先运行初始化函数,然后再进入while(1)死循环,运行俄罗斯方块游戏;

  1. void tetrisInit(void)
  2. {
  3. int i,j,k;
  4. HWND window = initgraph(510, 620); // 初始化窗口大小
  5. SetWindowText(window, "俄罗斯方块 - by耒阳阿杰"); // 设置当前窗口的标题
  6. //SetWindowPos(window,HWND_TOPMOST,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE); // 置顶
  7. rectangle(GAMEMAP1 - 2,GAMEMAP2 - 2,GAMEMAP3 + 2,GAMEMAP4 + 2); // 绘制游戏地图范围
  8. rectangle(GAMEMAP5 - 2,GAMEMAP6 - 2,GAMEMAP7 + 2,GAMEMAP8 + 2); // 绘制预生成方块方位
  9. rectangle(GAMEGUIDE1 - 5,GAMEGUIDE2 - 5,GAMEGUIDE3 + 5,GAMEGUIDE4 + 5); // 绘制按键说明
  10. outtextxy(GAMEMAP5 + 10 ,GAMEMAP6 - 26,"下个方块");
  11. settextcolor(GREEN);
  12. outtextxy(GAMEGUIDE1 ,GAMEGUIDE2 ,"W :改变形状");
  13. outtextxy(GAMEGUIDE1 ,GAMEGUIDE2 + 25 ,"A :方块左移");
  14. outtextxy(GAMEGUIDE1 ,GAMEGUIDE2 + 50 ,"S :方块右移");
  15. outtextxy(GAMEGUIDE1 ,GAMEGUIDE2 + 75 ,"D :方块下移");
  16. outtextxy(GAMEGUIDE1 ,GAMEGUIDE2 + 100 ,"R :重新开始");
  17. outtextxy(GAMEGUIDE1 ,GAMEGUIDE2 + 125 ,"ESC:退出游戏");
  18. // 初始化游戏数据
  19. memset ( &player, 0, sizeof ( GAME_TETRIS ) );
  20. memset ( &preBlock, 0, sizeof ( GAME_PREBLOCK ) );
  21. memset(tetrisMap,0,sizeof(tetrisMap));
  22. player.ttime = GetTickCount();
  23. // 获取随机数,生成第一个随机方块
  24. srand((unsigned)time(NULL)); // 获取随机数,生成新的随机方块
  25. i = (rand() + 1) % BLOCKTYPE;
  26. j = rand() % BLOCKSTYLE;
  27. for(k = 0; k < BLOCKSPACE;k++)
  28. {
  29. preBlock.tetrisPreBlock[k] = block[i][j][k];
  30. }
  31. preBlock.blockType = i;
  32. preBlock.blockStyle = j;
  33. // 把第一个随机方块加载进游戏,并生成下一个随机方块
  34. tetrisNewBlock();
  35. }

HWND window = initgraph(510, 620);    // 初始化窗口大小
SetWindowText(window, "俄罗斯方块 - by耒阳阿杰");    // 设置当前窗口的标题
//SetWindowPos(window,HWND_TOPMOST,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE);    // 置顶

1.初始化窗口,定义窗口大小,设置窗口标题,屏蔽的SetWindowPos()函数是用于给游戏置顶的,置顶之后,总是会显示在WINDOWS的最顶层,类似于微信的置顶;

rectangle(GAMEMAP1 - 2,GAMEMAP2 - 2,GAMEMAP3 + 2,GAMEMAP4 + 2);        // 绘制游戏地图范围
    rectangle(GAMEMAP5 - 2,GAMEMAP6 - 2,GAMEMAP7 + 2,GAMEMAP8 + 2);        // 绘制预生成方块方位
    rectangle(GAMEGUIDE1 - 5,GAMEGUIDE2 - 5,GAMEGUIDE3 + 5,GAMEGUIDE4 + 5);        // 绘制按键说明

    outtextxy(GAMEMAP5 + 10 ,GAMEMAP6 - 26,"下个方块");
    settextcolor(GREEN);
    outtextxy(GAMEGUIDE1 ,GAMEGUIDE2        ,"W   :改变形状");
    outtextxy(GAMEGUIDE1 ,GAMEGUIDE2 + 25    ,"A    :方块左移");
    outtextxy(GAMEGUIDE1 ,GAMEGUIDE2 + 50    ,"S    :方块右移");
    outtextxy(GAMEGUIDE1 ,GAMEGUIDE2 + 75    ,"D    :方块下移");
    outtextxy(GAMEGUIDE1 ,GAMEGUIDE2 + 100    ,"R    :重新开始");
    outtextxy(GAMEGUIDE1 ,GAMEGUIDE2 + 125    ,"ESC:退出游戏");

2.rectangle()函数是根据你输入的四个坐标,绘制一个矩形,这里绘制了游戏地图、预生成方块地图、按键说明框;

outtextxy(x,y,string)函数是根据你输入的起点坐标,还有后面的字符串,在相应位置打印字符串内容;

settextcolor(GREEN)函数是设置打印字符串的颜色;

// 初始化游戏数据
    memset ( &player, 0, sizeof ( GAME_TETRIS ) );
    memset ( &preBlock, 0, sizeof ( GAME_PREBLOCK ) );
    memset(tetrisMap,0,sizeof(tetrisMap));
    player.ttime = GetTickCount();

3. memset()函数是重置结构或者数据的数据,将他们置0;

然后给player.ttime赋值当时的时间,方便后面用来判定500ms周期。

// 获取随机数,生成第一个随机方块
    srand((unsigned)time(NULL));        // 获取随机数,生成新的随机方块
    i = (rand() + 1) % BLOCKTYPE;
    j = rand() % BLOCKSTYLE;
    for(k = 0; k < BLOCKSPACE;k++)
    {
        preBlock.tetrisPreBlock[k] = block[i][j][k];
    }
    preBlock.blockType = i;
    preBlock.blockStyle = j;
    // 把第一个随机方块加载进游戏,并生成下一个随机方块
    tetrisNewBlock();    

4.关于随机数生成,分为伪随机和真随机,这个建议读者们自己去查阅相关资料;

通过生成的随机数,再分别取方块类型和形状(朝向)的余数,就能得到一个随机方块的数据了;

得到第一个随机方块后,再执行 tetrisNewBlock()函数,把第一个随机方块载入游戏地图,再生成新的随机方块,载入预生成方块地图。

  • 2.tetrisDraw()

绘制函数,根据当前游戏数据,绘制整个游戏画面。

  1. void tetrisDraw(void)
  2. {
  3. int i,j;
  4. int x,y;
  5. char ch[20];
  6. // 更新RANK分数和LEVEL等级
  7. settextstyle(20,12,"宋体"); // 设置字体和大小
  8. settextcolor(CYAN); // 设置字体颜色
  9. if(player.level < 10)
  10. {
  11. sprintf_s(ch, "%s%d","LEVEL:", player.level);
  12. }
  13. else
  14. { // 等级为10时,显示最大max
  15. sprintf_s(ch, "%s","LEVEL:MAX");
  16. }
  17. outtextxy(GAMELEVEL1,GAMELEVEL2,ch);
  18. settextcolor(LIGHTBLUE);
  19. sprintf_s(ch, "%s%d","RANK:", player.rank);
  20. outtextxy(GAMERANK1,GAMERANK2,ch);
  21. // 绘制预生成方块的背景板
  22. setfillcolor(BLACK);
  23. solidrectangle(GAMEMAP5,GAMEMAP6,GAMEMAP7,GAMEMAP8);
  24. // 绘制预生成的方块
  25. for(i = 0; i < BLOCKSPACE; i++)
  26. {
  27. switch(preBlock.tetrisPreBlock[i])
  28. {
  29. case BLOCKNONE: //
  30. continue;
  31. break;
  32. case BLOCKBLUE: // 蓝色
  33. setfillcolor(BLUE);
  34. break;
  35. case BLOCKGREEN: // 绿色
  36. setfillcolor(GREEN);
  37. break;
  38. case BLOCKCYAN: // 青色
  39. setfillcolor(CYAN);
  40. break;
  41. case BLOCKRED: // 红色
  42. setfillcolor(RED);
  43. break;
  44. case BLOCKMAGENTA: // 深红色
  45. setfillcolor(MAGENTA);
  46. break;
  47. case BLOCKBROWN: // 灰色
  48. setfillcolor(BROWN);
  49. break;
  50. case BLOCKYELLOW:
  51. setfillcolor(YELLOW);
  52. break;
  53. }
  54. // 填充相应区域
  55. fillrectangle(GAMEMAP5 + (i%5) * SIDELENGTH ,GAMEMAP6 + (i/5) * SIDELENGTH ,GAMEMAP5 + ((i%5)+1)*SIDELENGTH ,GAMEMAP6 + ((i/5)+1) * SIDELENGTH );
  56. }
  57. // 绘制游戏地图背景板
  58. setfillcolor(BLACK);
  59. solidrectangle(GAMEMAP1,GAMEMAP2,GAMEMAP3,GAMEMAP4);
  60. // 绘制当前控制方块的4个方块格
  61. for(i = 0; i < 4; i++)
  62. {
  63. x = moveBlock.blockSite[i][0];
  64. y = moveBlock.blockSite[i][1];
  65. switch(moveBlock.blockColor)
  66. {
  67. case BLOCKNONE:
  68. continue;
  69. break;
  70. case BLOCKBLUE:
  71. setfillcolor(BLUE);
  72. break;
  73. case BLOCKGREEN:
  74. setfillcolor(GREEN);
  75. break;
  76. case BLOCKCYAN:
  77. setfillcolor(CYAN);
  78. break;
  79. case BLOCKRED:
  80. setfillcolor(RED);
  81. break;
  82. case BLOCKMAGENTA:
  83. setfillcolor(MAGENTA);
  84. break;
  85. case BLOCKBROWN:
  86. setfillcolor(BROWN);
  87. break;
  88. case BLOCKYELLOW:
  89. setfillcolor(YELLOW);
  90. break;
  91. }
  92. // 填充相应区域
  93. fillrectangle(GAMEMAP1 + y * SIDELENGTH ,GAMEMAP2 + x * SIDELENGTH ,GAMEMAP1 + (y+1)*SIDELENGTH ,GAMEMAP2 + (x+1) * SIDELENGTH );
  94. }
  95. // 绘制游戏实时画面
  96. for(i = 0; i < TETRISHEIGHT; i++)
  97. {
  98. for(j = 0; j < TETRISWIDTH; j++)
  99. {
  100. switch(tetrisMap[i][j])
  101. {
  102. case BLOCKNONE:
  103. continue;
  104. break;
  105. case BLOCKBLUE:
  106. setfillcolor(BLUE);
  107. break;
  108. case BLOCKGREEN:
  109. setfillcolor(GREEN);
  110. break;
  111. case BLOCKCYAN:
  112. setfillcolor(CYAN);
  113. break;
  114. case BLOCKRED:
  115. setfillcolor(RED);
  116. break;
  117. case BLOCKMAGENTA:
  118. setfillcolor(MAGENTA);
  119. break;
  120. case BLOCKBROWN:
  121. setfillcolor(BROWN);
  122. break;
  123. case BLOCKYELLOW:
  124. setfillcolor(YELLOW);
  125. break;
  126. }
  127. // 填充相应区域
  128. fillrectangle(GAMEMAP1 + j * SIDELENGTH ,GAMEMAP2 + i * SIDELENGTH ,GAMEMAP1 + (j+1)*SIDELENGTH ,GAMEMAP2 + (i+1) * SIDELENGTH );
  129. }
  130. }
  131. }

// 更新RANK分数和LEVEL等级
    settextstyle(20,12,"宋体");    // 设置字体和大小
    settextcolor(CYAN);            // 设置字体颜色
    if(player.level < 10)
    {
        sprintf_s(ch, "%s%d","LEVEL:", player.level);
    }
    else
    {    // 等级为10时,显示最大max
        sprintf_s(ch, "%s","LEVEL:MAX");
    }
    outtextxy(GAMELEVEL1,GAMELEVEL2,ch);
    settextcolor(LIGHTBLUE);
    sprintf_s(ch, "%s%d","RANK:", player.rank);
    outtextxy(GAMERANK1,GAMERANK2,ch);

1.设置输出字符的大小和字体,再设置颜色;

等级在10级以下时,照常显示;10级及以上时,显示MAX;

// 绘制预生成方块的背景板
    setfillcolor(BLACK);
    solidrectangle(GAMEMAP5,GAMEMAP6,GAMEMAP7,GAMEMAP8);

2.  setfillcolor(BLACK)设置填充颜色;solidrectangle(GAMEMAP5,GAMEMAP6,GAMEMAP7,GAMEMAP8) 根据四个坐标,使用设置好的填充颜色填充矩形区域,实际就是绘制一个带白色边框的黑色矩形。

// 绘制预生成的方块
    for(i = 0; i < BLOCKSPACE; i++)
    {
        switch(preBlock.tetrisPreBlock[i])
        {
            case BLOCKNONE:                // 空
                continue;
                break;
            case BLOCKBLUE:                // 蓝色
                setfillcolor(BLUE);
                break;
            case BLOCKGREEN:            // 绿色
                setfillcolor(GREEN);
                break;
            case BLOCKCYAN:                // 青色
                setfillcolor(CYAN);
                break;
            case BLOCKRED:                // 红色
                setfillcolor(RED);
                break;
            case BLOCKMAGENTA:            // 深红色
                setfillcolor(MAGENTA);
                break;
            case BLOCKBROWN:            // 灰色
                setfillcolor(BROWN);
                break;
            case BLOCKYELLOW:
                setfillcolor(YELLOW);
                break;
        }
        // 填充相应区域
        fillrectangle(GAMEMAP5 + (i%5) * SIDELENGTH ,GAMEMAP6 + (i/5) * SIDELENGTH ,GAMEMAP5 + ((i%5)+1)*SIDELENGTH ,GAMEMAP6 + ((i/5)+1) * SIDELENGTH );        
    }

3.绘制好预生成方块区域后,preBlock.tetrisPreBlock[i]是一个包含20个成员的数组,里面有16个空成员,不用管;还有4个成员就是要绘制的方块。用switch选择相应的填充颜色setfillcolor(),然后再执行 fillrectangle()函数,在相应坐标填充方块;

最后的填充函数,可能会有点复杂,这里解释一下:

预生成方块区域是一个100X80像素大小的矩形,每个方块都是20X20像素大小,所以矩形按20X20像素切割,得到一个5X4的坐标轴,包含20个方块,刚好和所有方块数据库里的方块对应。

如下图所示,只需要把数据库里20个数据中,包含方块的四个数据选出来填入,就能得到一个完整的方块了。

现在,我想要绘制一个 '----' 型方块,只需要填充2,3,4,5这4个坐标,就能得到方块;

那么它在preBlock.tetrisPreBlock[]数组中对应的数据就是

{0,1,1,1,1, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0};得到的最终图像就是如下所示

当preBlock.tetrisPreBlock[i]为1时,就会执行fillrectangle()函数,根据i的值,获得四个坐标值,填充这个方块;

第一个方块:i = 1,  fillrectangle(GAMEMAP5 + 1 * 20,GAMEMAP6,GAMEMAP5 + 2*20,GAMEMAP6 + 1 * 20); 

GAMEMAP5和GAMEMAP6就是预生成方块区域的坐上角坐标,将它视为起点坐标,忽略掉,

上面的fillrectangle() 就相当于fillrectangle( 20,0, 40,20),绘制出来就是如下图

 第二个方块:i = 2,fillrectangle(GAMEMAP5 + 2 * 20,GAMEMAP6  ,GAMEMAP5 + 3*20,GAMEMAP6 + 1 * 20);  

忽略起点坐标,得到fillrectangle( 40,0,60,20),绘制出来如下图

 依次画出第三个方块,第四个方块,就能得到最终的图像了

// 绘制游戏地图背景板
    setfillcolor(BLACK);
    solidrectangle(GAMEMAP1,GAMEMAP2,GAMEMAP3,GAMEMAP4);

4.和2类似,绘制一个白色边框的纯黑矩形

// 绘制当前控制方块的4个方块格
    for(i = 0; i < 4; i++)
    {
        x = moveBlock.blockSite[i][0];
        y = moveBlock.blockSite[i][1];
        switch(moveBlock.blockColor)
        {
            case BLOCKNONE:
                continue;
                break;
            case BLOCKBLUE:
                setfillcolor(BLUE);
                break;
            case BLOCKGREEN:
                setfillcolor(GREEN);
                break;
            case BLOCKCYAN:
                setfillcolor(CYAN);
                break;
            case BLOCKRED:
                setfillcolor(RED);
                break;
            case BLOCKMAGENTA:
                setfillcolor(MAGENTA);
                break;
            case BLOCKBROWN:
                setfillcolor(BROWN);
                break;
            case BLOCKYELLOW:
                setfillcolor(YELLOW);
                break;
        }
        // 填充相应区域
        fillrectangle(GAMEMAP1 + y * SIDELENGTH ,GAMEMAP2 + x * SIDELENGTH ,GAMEMAP1 + (y+1)*SIDELENGTH ,GAMEMAP2 + (x+1) * SIDELENGTH );        
    }

5.和3类似,变量moveBlock.blockSite[]保存了当前方块的四个方块格坐标,根据方块颜色moveBlock.blockColor,通过switch选择相应的颜色,然后fillrectangle填充,得到当前方块。

// 绘制游戏实时画面
    for(i = 0; i < TETRISHEIGHT; i++)
    {
        for(j = 0; j < TETRISWIDTH; j++)
        {    
            switch(tetrisMap[i][j])
            {
                case BLOCKNONE:
                    continue;
                    break;
                case BLOCKBLUE:
                    setfillcolor(BLUE);
                    break;
                case BLOCKGREEN:
                    setfillcolor(GREEN);
                    break;
                case BLOCKCYAN:
                    setfillcolor(CYAN);
                    break;
                case BLOCKRED:
                    setfillcolor(RED);
                    break;
                case BLOCKMAGENTA:
                    setfillcolor(MAGENTA);
                    break;
                case BLOCKBROWN:
                    setfillcolor(BROWN);
                    break;
                case BLOCKYELLOW:
                    setfillcolor(YELLOW);
                    break;
            }
            // 填充相应区域
            fillrectangle(GAMEMAP1 + j * SIDELENGTH ,GAMEMAP2 + i * SIDELENGTH ,GAMEMAP1 + (j+1)*SIDELENGTH ,GAMEMAP2 + (i+1) * SIDELENGTH );        
        }
    }

6.和3、5类似,tetrisMap[]数组保存了游戏地图中已经固定下来的方块和颜色,通过switch选择颜色,然后使用fillrectangle()函数,绘制所有固定方块。

  • 3.tetrisNewBlock() 函数

当新方块标识player.newBlockFlag为1时,会执行本函数,把预生成方块加载进游戏,成为当前控制方块;之后再获取随机数,生成新的预生成方块。

  1. void tetrisNewBlock(void)
  2. {
  3. int i,j,k;
  4. // 把上一个随机方块加载进当前方块数组
  5. k = 0;
  6. for(i = 0; i < BLOCKSPACE; i++)
  7. {
  8. if(preBlock.tetrisPreBlock[i])
  9. {
  10. moveBlock.blockColor = preBlock.tetrisPreBlock[i]; // 记录方块颜色
  11. moveBlock.blockSite[k][0] = i/5; // 记录方块格的X坐标
  12. moveBlock.blockSite[k][1] = 5 + i%5; // 记录方块格的Y坐标
  13. k++;
  14. }
  15. }
  16. player.blockType = preBlock.blockType; // 记录方块的类型
  17. player.blockStyle = preBlock.blockStyle; // 记录方块的形状(朝向)
  18. // 获取随机数,生成新的随机方块
  19. srand((unsigned)time(NULL));
  20. i = rand() % BLOCKTYPE;
  21. j = rand() % BLOCKSTYLE;
  22. for(k = 0; k < BLOCKSPACE;k++)
  23. {
  24. preBlock.tetrisPreBlock[k] = block[i][j][k];
  25. }
  26. preBlock.blockType = i;
  27. preBlock.blockStyle = j;
  28. }

// 把上一个随机方块加载进当前方块数组
    k = 0;
    for(i = 0; i < BLOCKSPACE; i++)
    {
        if(preBlock.tetrisPreBlock[i])
        {
            moveBlock.blockColor = preBlock.tetrisPreBlock[i];    // 记录方块颜色
            moveBlock.blockSite[k][0] = i/5;                    // 记录方块格的X坐标
            moveBlock.blockSite[k][1] = 5 + i%5;                // 记录方块格的Y坐标
            k++;
        }
    }
    player.blockType = preBlock.blockType;        // 记录方块的类型
    player.blockStyle = preBlock.blockStyle;    // 记录方块的形状(朝向)

1.preBlock.tetrisPreBlock[i]数组有16个成员为0,还有4个非0成员(方块格),检测到非0成员(方块格)时,就会记录方块颜色、坐标、类型、形状(朝向),保存到当前方块变量moveBlock。

// 获取随机数,生成新的随机方块
    srand((unsigned)time(NULL));        
    i = rand() % BLOCKTYPE;
    j = rand() % BLOCKSTYLE;
    for(k = 0; k < BLOCKSPACE;k++)
    {
        preBlock.tetrisPreBlock[k] = block[i][j][k];
    }
    preBlock.blockType = i;
    preBlock.blockStyle = j;

2.生成随机数,然后得到方块类型和形状(朝向),再把方块数据库中对应的20个成员值复制到preBlock.tetrisPreBlock数组中,就成为了一个新的随机方块。

4.tetrisMoveUp() 函数

当玩家按下W键后,会执行此函数,如果当前方块可改变形状(朝向),则改变形状(朝向)。

  1. void tetrisMoveUp(void)
  2. {
  3. int i,k;
  4. int bStyle;
  5. int ux,uy; // 改变方块朝向前后,方块格的位移矢量
  6. int blocksite2[4][2]; // 记录改变朝向前,4个方块的方块库坐标
  7. int blocksite3[4][2]; // 记录改变朝向后,4个方块的方块库坐标
  8. int blocksite4[4][2]; // 记录改变朝向后,4个方块的游戏地图坐标
  9. // 找到4个方块格的方块库坐标
  10. k = 0;
  11. for(i = 0; i < BLOCKSPACE; i++)
  12. {
  13. // 得到4个方块坐标后,退出循环
  14. if(k > 3)
  15. {
  16. break;
  17. }
  18. if(block[player.blockType][player.blockStyle][i])
  19. {
  20. blocksite2[k][0] = i / 5;
  21. blocksite2[k][1] = i % 5;
  22. k++;
  23. }
  24. }
  25. // 找到改变朝向后,4个方块格的方块库坐标
  26. k = 0;
  27. bStyle = (player.blockStyle + 1)%BLOCKSTYLE; // 获取改变朝向后的方块形状
  28. for(i = 0; i < BLOCKSPACE; i++)
  29. {
  30. // 得到4个方块坐标后,退出循环
  31. if(k > 3)
  32. {
  33. break;
  34. }
  35. if(block[player.blockType][bStyle][i])
  36. {
  37. blocksite3[k][0] = i / 5;
  38. blocksite3[k][1] = i % 5;
  39. k++;
  40. }
  41. }
  42. // 根据改变前后方块坐标的位移矢量,得到改变朝向后的当前控制方块的新坐标
  43. for(i = 0; i < 4; i++)
  44. {
  45. ux = blocksite3[i][0] - blocksite2[i][0]; // 获取方格的X位移矢量
  46. uy = blocksite3[i][1] - blocksite2[i][1]; // 获取方格的Y位移矢量
  47. blocksite4[i][0] = moveBlock.blockSite[i][0] + ux; // 得到改变朝向后的,当前控制方块格,位于游戏地图的新X坐标
  48. blocksite4[i][1] = moveBlock.blockSite[i][1] + uy; // 得到改变朝向后的,当前控制方块格,位于游戏地图的新Y坐标
  49. if( (blocksite4[i][0] < 0) // 判断新坐标是否超出游戏地图,超出则终止改变方块朝向
  50. || (blocksite4[i][1] < 0)
  51. || (blocksite4[i][0] > TETRISHEIGHT - 1)
  52. || (blocksite4[i][1] > TETRISWIDTH - 1)
  53. || ( tetrisMap[blocksite4[i][0]][blocksite4[i][1]] ) ) // 如果新坐标在游戏地图中已存在方块格,则终止改变方块朝向
  54. {
  55. return;
  56. }
  57. }
  58. // 改变方块朝向没有问题,则更新新方块的坐标
  59. for(i = 0;i < 4; i++)
  60. {
  61. moveBlock.blockSite[i][0] = blocksite4[i][0];
  62. moveBlock.blockSite[i][1] = blocksite4[i][1];
  63. }
  64. player.blockStyle = bStyle; // 更新方块格形状
  65. }

// 找到4个方块格的方块库坐标
    k = 0;
    for(i = 0; i < BLOCKSPACE; i++)
    {
        // 得到4个方块坐标后,退出循环
        if(k > 3)
        {    
            break;
        }
        if(block[player.blockType][player.blockStyle][i])
        {
            blocksite2[k][0] = i / 5;
            blocksite2[k][1] = i % 5;
            k++;
        }
    }

1.根据当前控制方块的形状(朝向),在方块数据库中找到对应的坐标,并放入数组blocksite2中。

// 找到改变朝向后,4个方块格的方块库坐标
    k = 0;
    bStyle = (player.blockStyle + 1)%BLOCKSTYLE;    // 获取改变朝向后的方块形状
    for(i = 0; i < BLOCKSPACE; i++)
    {
        // 得到4个方块坐标后,退出循环
        if(k > 3)
        {    
            break;
        }
        if(block[player.blockType][bStyle][i])
        {
            blocksite3[k][0] = i / 5;
            blocksite3[k][1] = i % 5;
            k++;
        }
    }

2. player.blockStyle是当前方块的形状(朝向),只要把这个变量加1,再取余方块形状数,就可以得到新的方块形状(朝向)bStyle;根据方块类型,还有新的朝向bStyle,就可以从方块数据库中得到新方块的坐标,把新方块的坐标放入blocksite3[]数组。

// 根据改变前后方块坐标的位移矢量,得到改变朝向后的当前控制方块的新坐标
    for(i = 0; i < 4; i++)
    {
        ux = blocksite3[i][0] - blocksite2[i][0];    // 获取方格的X位移矢量
        uy = blocksite3[i][1] - blocksite2[i][1];    // 获取方格的Y位移矢量

        blocksite4[i][0] = moveBlock.blockSite[i][0] + ux;    // 得到改变朝向后的,当前控制方块格,位于游戏地图的新X坐标
        blocksite4[i][1] = moveBlock.blockSite[i][1] + uy;    // 得到改变朝向后的,当前控制方块格,位于游戏地图的新Y坐标
        
        if( (blocksite4[i][0] < 0)                    // 判断新坐标是否超出游戏地图,超出则终止改变方块朝向
            || (blocksite4[i][1] < 0)
            || (blocksite4[i][0] > TETRISHEIGHT - 1)
            || (blocksite4[i][1] > TETRISWIDTH - 1)
            || ( tetrisMap[blocksite4[i][0]][blocksite4[i][1]] ) )    // 如果新坐标在游戏地图中已存在方块格,则终止改变方块朝向
        {
            return;
        }
    }

3.既然通过1、2步骤,得到了方块改变形状前后,位于方块数据库中的坐标,那么就可以通过把新旧坐标相减,得到旧坐标的位移矢量ux,uy;

通过位移矢量ux,uy,还有当前方块的四个方块格坐标moveBlock.blockSite,就可以得到改变形状(朝向)后,当前方块的新坐标,并把新坐标放入blocksite4[][]数组中;

有了新坐标blocksite4,就可以判断当前方块是否可以改变形状(朝向),首先判断新坐标是否超过游戏地图范围,再判断新坐标位置,是否已经存在方块;只要有一个不符合,那么本次操作无效,退出函数。

// 改变方块朝向没有问题,则更新新方块的坐标
    for(i = 0;i < 4; i++)
    {
        moveBlock.blockSite[i][0] = blocksite4[i][0];
        moveBlock.blockSite[i][1] = blocksite4[i][1];
    }
    player.blockStyle = bStyle;    // 更新方块格形状

4.如果方块可以改变形状(朝向),那么就把方块的新坐标blocksite4,赋值给当前方块坐标moveBlock.blockSite;再把新的方块形状(朝向)bStyle,赋值给当前方块形状player.blockStyle

5.tetrisMoveDown() 函数

当方块下移标识player.moveFlag为1时,会执行本函数,控制当前方块下移。

player.moveFlag为1有两种条件

1:每个周期(默认500ms)会赋值为1。

2:用户按下S键,会赋值为1。

  1. void tetrisMoveDown(void)
  2. {
  3. int i;
  4. int x,y;
  5. // 判断当前方块是否到达地图底部
  6. for(i = 0; i < 4; i++)
  7. {
  8. if(moveBlock.blockSite[i][0] > TETRISHEIGHT - 2)
  9. {
  10. // 如果到达地图底部,把生成新方块标识置1
  11. player.newBlockFlag = TRUE;
  12. return;
  13. }
  14. }
  15. // 判断当前方块能否下移,是否会和游戏地图已存在的方块产生冲突
  16. for(i = 0; i < 4; i++)
  17. {
  18. x = moveBlock.blockSite[i][0] + 1; // 获取方块下移后的新X坐标
  19. y = moveBlock.blockSite[i][1]; // 获取方块下移后的新Y坐标
  20. // 如果新坐标在游戏地图中,已经存在方块格,则方块停止移动
  21. if(tetrisMap[x][y])
  22. {
  23. player.newBlockFlag = TRUE;
  24. return;
  25. }
  26. }
  27. // 当前方块下移
  28. for(i = 0; i < 4; i++)
  29. {
  30. moveBlock.blockSite[i][0]++;
  31. }
  32. }

// 判断当前方块是否到达地图底部
    for(i = 0; i < 4; i++)
    {
        if(moveBlock.blockSite[i][0] > TETRISHEIGHT - 2)
        {
            // 如果到达地图底部,把生成新方块标识置1
            player.newBlockFlag = TRUE;
            return;
        }
    }

1.每次方块下移时,都会先判断方块是否到达游戏地图底部,如果到达底部,会给新方块标识player.newBlockFlag赋1,然后退出本函数,当前方块不再移动。

// 判断当前方块能否下移,是否会和游戏地图已存在的方块产生冲突
    for(i = 0; i < 4; i++)
    {
        x = moveBlock.blockSite[i][0] + 1;    // 获取方块下移后的新X坐标
        y = moveBlock.blockSite[i][1];        // 获取方块下移后的新Y坐标
        // 如果新坐标在游戏地图中,已经存在方块格,则方块停止移动
        if(tetrisMap[x][y])
        {
            player.newBlockFlag = TRUE;
            return;
        }
    }

2.方块下移会改变moveBlock.blockSite[][]数组的X坐标,加一;

判断游戏地图中新坐标位置是否已存在方块,如果存在方块,会给新方块标识player.newBlockFlag赋1,然后退出本函数,当前方块不再移动。

// 当前方块下移
    for(i = 0; i < 4; i++)
    {
        moveBlock.blockSite[i][0]++;
    }

3.方块可以下移,则将当前方块的4个方块格的X坐标都加一。

6.tetrisMoveLeft() 函数

每当用户按下A键,会执行本函数,控制当前方块左移。

  1. void tetrisMoveLeft(void)
  2. {
  3. int i;
  4. int x,y;
  5. // 先判断是否能左移
  6. for(i = 0; i < 4; i++)
  7. {
  8. x = moveBlock.blockSite[i][0];
  9. y = moveBlock.blockSite[i][1];
  10. // 将Y坐标减一,再判断是否超出地图范围或引起冲突
  11. if( (y - 1) < 0 // 1.超出地图范围
  12. || tetrisMap[x][y - 1]) // 2.方块将要移动的地方已存在方块格
  13. {
  14. return; // 超出地图范围或冲突,无法左移
  15. }
  16. }
  17. // 左移
  18. for(i = 0; i < 4; i++)
  19. {
  20. moveBlock.blockSite[i][1]--;
  21. }
  22. }

// 先判断是否能左移
    for(i = 0; i < 4; i++)
    {
        x = moveBlock.blockSite[i][0];
        y = moveBlock.blockSite[i][1];
        // 将Y坐标减一,再判断是否超出地图范围或引起冲突
        if( (y - 1) < 0                // 1.超出地图范围
            || tetrisMap[x][y - 1])    // 2.方块将要移动的地方已存在方块格
        {
            return;    // 超出地图范围或冲突,无法左移
        }
    }

1.方块左移,就是把方块的Y坐标减一,然后判断新的坐标是否超过游戏地图范围、新的坐标是否已经存在方块;只要有一个条件不满足,就会退出本函数,无法左移。

// 左移
    for(i = 0; i < 4; i++)
    {
        moveBlock.blockSite[i][1]--;
    }

 2.可以左移,则将当前方块的四个方块格的Y坐标都减一。

7.tetrisMoveRight() 函数

每当用户按下D键,会执行本函数,控制当前方块右移。

和左移函数基本相同,就是Y坐标变为加1,因此不再过多说明。

  1. void tetrisMoveRight(void)
  2. {
  3. int i;
  4. int x,y;
  5. //k = 0;
  6. // 先判断是否能右移
  7. for(i = 0; i < 4; i++)
  8. {
  9. x = moveBlock.blockSite[i][0];
  10. y = moveBlock.blockSite[i][1];
  11. // 将Y坐标加1,再判断是否超出地图范围或冲突
  12. if( ((y + 1) >= TETRISWIDTH) // 1.超出地图范围
  13. || (tetrisMap[x][y + 1]) ) // 2.方块将要移动的地方已存在方块格
  14. {
  15. return; // 超出地图范围或冲突,无法左移
  16. }
  17. }
  18. // 右移
  19. for(i = 0; i < 4; i++)
  20. {
  21. moveBlock.blockSite[i][1]++;
  22. }
  23. }

8.tetrisLoadBlock() 函数

每当新方块标识player.newBlockFlag为1时,执行本函数,将当前控制方块固定在游戏地图。

  1. void tetrisLoadBlock(void)
  2. {
  3. int i;
  4. int x,y;
  5. // 将当前方块载入游戏地图
  6. for(i = 0; i < 4; i++)
  7. {
  8. x = moveBlock.blockSite[i][0];
  9. y = moveBlock.blockSite[i][1];
  10. // 只有游戏地图坐标不为空,才会改变游戏地图
  11. if(!tetrisMap[x][y])
  12. {
  13. tetrisMap[x][y] = moveBlock.blockColor;
  14. }
  15. }
  16. }

9.tetrisIsOver() 函数

每当新方块标识player.newBlockFlag为1时,执行本函数,判断游戏是否结束

  1. void tetrisIsOver(void)
  2. {
  3. int i;
  4. int x,y;
  5. // 将新的移动方块与游戏地图比较,如果有冲突格,则游戏结束
  6. for(i = 0; i < 4; i++)
  7. {
  8. x = moveBlock.blockSite[i][0];
  9. y = moveBlock.blockSite[i][1];
  10. // 如果移动方块和游戏地图在同一坐标都存在方块,则游戏结束
  11. if(tetrisMap[x][y])
  12. {
  13. player.gameOverFlag = TRUE;
  14. tetrisDraw();
  15. tetrisQuit();
  16. return;
  17. }
  18. }
  19. }

将预生成方块载入游戏地图时,和游戏地图比较;预生成方块和游戏地图已存在的方块重叠,则预生成方块无法载入游戏地图,游戏无法继续,判断游戏结束。

将游戏结束标识player.gameOverFlag赋1,接着执行tetrisDraw()函数,绘制重叠方块;再执行tetrisQuit()函数,由用户选择是退出游戏还是重新开始游戏。

10.tetrisQuit() 函数

本函数执行有两种方式,一:游戏结束,玩家已无法再继续游戏。

二:玩家按下了ESC键,想要主动退出游戏。

  1. void tetrisQuit(void)
  2. {
  3. int key,flag;
  4. flag = player.gameOverFlag;
  5. // 让用户选择是否退出
  6. key = MessageBox(NULL,"是否退出游戏?","提示",MB_YESNO| MB_SYSTEMMODAL);
  7. switch(key)
  8. {
  9. case IDYES:
  10. player.gameOverFlag = TRUE;
  11. break;
  12. case IDNO:
  13. player.gameOverFlag = FALSE;
  14. break;
  15. default:
  16. break;
  17. }
  18. // 如果游戏已无法继续,且用户不退出游戏,则强制用户选择结束游戏,或重新开始游戏
  19. while(flag
  20. && !player.gameOverFlag)
  21. {
  22. key = MessageBox(NULL,"检测到游戏已无法继续,\n请选择\"是\": 退出游戏;\n或者选择\"否\": 重启游戏;","提示",MB_YESNO| MB_SYSTEMMODAL);
  23. switch(key)
  24. {
  25. case IDYES:
  26. player.gameOverFlag = TRUE;
  27. break;
  28. case IDNO:
  29. tetrisInit();
  30. player.gameOverFlag = FALSE;
  31. return;
  32. default:
  33. break;
  34. }
  35. }
  36. CLEARKEY();
  37. }

1.flag = player.gameOverFlag;
    // 让用户选择是否退出
    key = MessageBox(NULL,"是否退出游戏?","提示",MB_YESNO| MB_SYSTEMMODAL);
    switch(key)
    {
        case IDYES:
            player.gameOverFlag = TRUE;
            break;
        case IDNO:
            player.gameOverFlag = FALSE;
            break;
        default:
            break;
    }

1.flag标识用于区分是游戏结束,还是玩家主动结束游戏,不同方式有不同处理。

生成一个消息弹窗,MB_SYSTEMMODAL是强制弹窗置顶,功能类似于微信置顶功能。

key是用于得到用户选择“是”还是“否”,如果玩家选择退出游戏, 将游戏结束标识player.gameOverFlag置1;反之置0。

// 如果游戏已无法继续,且用户不退出游戏,则强制用户选择结束游戏,或重新开始游戏
    while(flag
        && !player.gameOverFlag)
    {
        key = MessageBox(NULL,"检测到游戏已无法继续,\n请选择\"是\": 退出游戏;\n或者选择\"否\": 重启游戏;","提示",MB_YESNO| MB_SYSTEMMODAL);
        switch(key)
        {
            case IDYES:
                player.gameOverFlag = TRUE;
                break;
            case IDNO:
                tetrisInit();
                player.gameOverFlag = FALSE;
                return;
            default:
                break;
        }
    }
    CLEARKEY();

2.如果是用户主动结束游戏,那么flag为0,不会进入循环;

如果是游戏已无法继续进行,那么flag为1,会进入循环。

如果进入循环,会生成一个消息弹窗,提示用户是退出游戏,还是重新开始游戏并获取用户选择结果。

通过key获取用户选择, 如果用户选择“是”,那么再次赋值player.gameOverFlag = TRUE,接着退出此函数。

如果用户选择“否”,那么会执行初始化函数tetrisInit(),并赋值player.gameOverFlag = FALSE;

重新开始游戏。

CLEARKEY();是一个宏定义,用于清除按键输入缓冲区。

11.tetrisKeyHandle() 函数

用户按键处理函数,每次用户按下相应按键后,都会执行本函数。

其中的W键改变方块形状(朝向)、A键左移、S键下移、D键右移、ESC退出键都已经在上面的函数说明了,这里就不再复述。

  1. void tetrisKeyHandle(void)
  2. {
  3. switch(player.key)
  4. {
  5. case 'w':
  6. case 'W':
  7. // W键,改变当前方块的朝向
  8. tetrisMoveUp();
  9. break;
  10. case 'a':
  11. case 'A':
  12. // A键,当前方块想左移动
  13. tetrisMoveLeft();
  14. break;
  15. case 's':
  16. case 'S':
  17. // S键,当前方块向下移动
  18. player.moveFlag = TRUE;
  19. player.ttime = GetTickCount();
  20. return;
  21. case 'd':
  22. case 'D':
  23. // D键,当前方块向右移动
  24. tetrisMoveRight();
  25. break;
  26. case ESC:
  27. // 退出键,退出游戏
  28. tetrisQuit();
  29. break;
  30. case 'r':
  31. case 'R':
  32. // R键,重置游戏进度
  33. tetrisReset();
  34. break;
  35. default:
  36. break;
  37. }
  38. // 按键之后,刷新地图
  39. tetrisDraw();
  40. }

1.就剩一个R键重新开始游戏了;

按下R键后,会执行tetrisReset()函数,进行游戏重置,函数详情会在下一个函数说明。

12.tetrisReset() 函数

重置游戏函数,玩家按下R键后,可以选择是否重新开始游戏。

  1. void tetrisReset(void)
  2. {
  3. int key;
  4. key = MessageBox(NULL, "是否重新开始游戏?", "提示", MB_YESNO | MB_SYSTEMMODAL);
  5. CLEARKEY();
  6. switch (key)
  7. {
  8. case IDYES:
  9. tetrisInit();
  10. break;
  11. case IDNO:
  12. break;
  13. default:
  14. break;
  15. }
  16. }

生成一个置顶弹窗,用变量key获取用户点击的按钮(“是”“否”);

如果用户点击的是“是”,则执行tetrisInit()函数,初始化所有游戏数据,重新开始游戏。

如果用户点击的是“否”,则取消重置游戏,本次按键无效。

13.tetrisRemove() 函数

最后一个函数,消除满行函数,每次生成新方块标识player.newBlockFlag置1时,会执行本函数。

  1. void tetrisRemove(void)
  2. {
  3. int i, j, m, n;
  4. int flag, bnum;
  5. bnum = 0;
  6. // 从最下面一行开始,检测并消除满行方块
  7. for (i = TETRISHEIGHT - 1; i >= 0; i--)
  8. {
  9. flag = 0;
  10. for (j = 0; j < TETRISWIDTH; j++)
  11. {
  12. // 只要检测到空格,就不再检测本行
  13. if (!tetrisMap[i][j])
  14. {
  15. flag = 1;
  16. break;
  17. }
  18. }
  19. if (flag)
  20. {
  21. continue;
  22. }
  23. // 满行,消除行数加1
  24. bnum++;
  25. // 检测到满行,消除当前行
  26. for (j = 0; j < TETRISWIDTH; j++)
  27. {
  28. tetrisMap[i][j] = BLOCKNONE;
  29. }
  30. // 消除行上面的所有方块下移一行
  31. for (m = i; m > 0; m--)
  32. {
  33. for (n = 0; n < TETRISWIDTH; n++)
  34. {
  35. tetrisMap[m][n] = tetrisMap[m - 1][n];
  36. }
  37. }
  38. i++; // 由于消除行上面的方块下移了,所以再从当前行检测
  39. }
  40. // 消除行数为0,退出
  41. if (!bnum)
  42. {
  43. return;
  44. }
  45. // 根据消除的行数,增加RANK分数
  46. switch (bnum)
  47. {
  48. case 0:
  49. break;
  50. case 1:
  51. player.rank += 10;
  52. break;
  53. case 2:
  54. player.rank += 20;
  55. break;
  56. case 3:
  57. player.rank += 40;
  58. break;
  59. case 4:
  60. player.rank += 80;
  61. break;
  62. default:
  63. break;
  64. }
  65. // 根据rank分数换算成level
  66. player.level = player.rank / 100;
  67. if (player.level > 10)
  68. {
  69. player.level = 10;
  70. }
  71. }

// 从最下面一行开始,检测并消除满行方块
    for (i = TETRISHEIGHT - 1; i >= 0; i--)
    {
        flag = 0;
        for (j = 0; j < TETRISWIDTH; j++)
        {
            // 只要检测到空格,就不再检测本行
            if (!tetrisMap[i][j])
            {
                flag = 1;
                break;
            }
        }
        if (flag)
        {
            continue;
        }
        // 满行,消除行数加1
        bnum++;
        // 检测到满行,消除当前行
        for (j = 0; j < TETRISWIDTH; j++)
        {
            tetrisMap[i][j] = BLOCKNONE;
        }
        // 消除行上面的所有方块下移一行
        for (m = i; m > 0; m--)
        {
            for (n = 0; n < TETRISWIDTH; n++)
            {
                tetrisMap[m][n] = tetrisMap[m - 1][n];
            }
        }
        i++;        // 由于消除行上面的方块下移了,所以再从当前行检测
    }

1.从游戏地图最下面一行开始检测,依次检测到最上面一行;只要检测到一个空白格,则放弃检测本行,检测上一行。

如果当前行是满行,则消除行数bnum加一;然后把当前行清零;清零后,把当前行上面的所有方块全部下移一行;i++是因为所有方块都下移一行了,需要再次从当前行开始检测。

// 消除行数为0,退出
    if (!bnum)
    {
        return;
    }
    // 根据消除的行数,增加RANK分数
    switch (bnum)
    {
    case 0:
        break;
    case 1:
        player.rank += 10;
        break;
    case 2:
        player.rank += 20;
        break;
    case 3:
        player.rank += 40;
        break;
    case 4:
        player.rank += 80;
        break;
    default:
        break;
    }
    // 根据rank分数换算成level
    player.level = player.rank / 100;
    if (player.level > 10)
    {
        player.level = 10;
    }

2.如果没有检测到可消除行(bnum为0),则退出本函数。

根据消除行数bnum,增加player.rank分数:

1行加10分;2行加20分;3行加40分;4行加80。

根据player.rank分数,换算成player.level等级:

1000分以下,等级是分数/100;1000分以上,等级固定为10(MAX)。


总结

至此,代码部分已经结束,从编写俄罗斯方块到结束,代码有经历过各种优化,bug也基本都修正了,可以说是一个比较完善的版本(基本没有BUG)。有任何疑问都可以在评论里提问,谢谢观看。

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

闽ICP备14008679号