C++学习之基于win32控制台的-贪吃蛇游戏

使用c++,编写了个贪吃蛇游戏,先看效果图如下:

程序实现速度等级选择、得分显示、中途暂停、重新开始等功能。

平台:win32控制平台,VC6.0。

实现过程:

1)、创建两个类:游戏类和蛇类。

2)、游戏类实现游戏版图空间和版图操作坐标功能,蛇类丰富版图显示并提供游戏进行的操作方法。

3)、蛇类中具备版图构建、开始游戏、退出游戏、创建蛇躯、蛇运动、随机生产食物、实时消息显示等方法。

4)、游戏运行时,系统分为准备、开始、运行、暂停、挂起、退出等状态,根据状态不同执行不同操作。

  准备:初始化游戏状态,构建游戏显示的版图,具备初始蛇身。

  开始:输入具体数度等级(1~9级)后开始。

  运行:蛇吃食物,并随机产生食物。

  暂停:空格键可以暂停/启动游戏。

  挂起:当游戏结束时,处于挂起状态,任意键重新开始,即回到准备态。

  退出:结束程序。

操作要点:

1)、由于使用char型字符显示蛇身横竖间距不一致,影响美观,构建版图时使用宽字符创建。即定义wchar_t型的空间。wchar_t数据需要使用std::wcoutwprintf()函数输出,并且输出时还要设置本地语言,使用setlocale(LC_ALL, "chs")设置语言为中文,不然输出会异常。

2)、对光标的处理使用系统函数,具体方法详见代码。

3)、键盘值捕获使用getch()函数捕获一个字节,其中字符输入捕获是一个字节,而方向键是两个字节,前一个表示功能键(0或224),后一个为键值(上72|下80|左85|右77);游戏运行过程中需要实时获取按键,而getch()函数是阻塞的,此时需要使用kbhit()函数(头文件:conio.h)判断是否有按键按下,有按键按下时才获取键值。

4)、蛇的食物随机数的产生使用rand()产生一个随机数,用rand() % SIZE的方法限定随机数的范围,由于rand产生的随机数是伪随机数,需使用srand(time(0))初始化随机数发生器,time(0)是一个跟时间有关的数值,每次都不同,从而产生了真正的随机数。

5)、蛇的方向是不能后退的,故使用abs()取两次键值的差值,不为1时有效,定义方向枚举时,上下与左右不应连续。

6)、蛇的活动空间为定义buf[LEN][LEN]的方形空间,蛇身数据定义为buf[(LEN-2)*(LEN-2)],该区间应为蛇可以活动的所有空间的大小。蛇身数组中保存所有的位置数据,通过头和尾两个下标值来循环读取。修改宏LEN的值就可以调节游戏版图大小,后续的所有绘制都应以这个为基准。

7)、通过修改字符变量可以直接修改蛇的形状和围墙的形状。

wchar_t snakeHead[] = L"■";
wchar_t snakeBody[] = L"□";
wchar_t snakeFood[] = L"★";
wchar_t mapWall[] = L"■";

待升级处理:

1)、实现蛇类设置接口的操作方法,可以进行游戏的设置,如速度选择、蛇或围墙的型状的选择。

2)、可以在版图中绘制阻碍围墙,以实现不同模式。

3)、根据游戏进行的程度,设置彩蛋,如提示之类的显示。

4)、游戏过程中不断提升数度模式。

5)、代码编排,类型定义和方法实现使用不同文件,不同类使用不同文件。丰富游戏类接口和蛇类接口。

  对消息方法进行重载以实现不同消息输出。

实现代码:

  1 #include <iostream>
  2 #include <conio.h>
  3 #include <windows.h>
  4 #include <iomanip>
  5 #include <time.h>
  6 using namespace std;
  7 
  8 #define DISPLAY_LEN  22   // 显示区域列
  9 #define DISPLAY_LEN  22   // 显示区域列
 10 #define SNAKE_SIZE       ((DISPLAY_LEN-2)*(DISPLAY_LEN-2))
 11 
 12 // 各信息输入起点
 13 #define SPEED_X            (DISPLAY_LEN*2+10)
 14 #define SPEED_Y            3
 15 #define GRADE_X            (DISPLAY_LEN*2+10)
 16 #define GRADE_Y            5
 17 #define STOP_REMIND_X    (DISPLAY_LEN*2+10)
 18 #define STOP_REMIND_Y    7
 19 #define MSG_X            (DISPLAY_LEN*2+7)
 20 #define MSG_Y            (DISPLAY_LEN -2)
 21 
 22 wchar_t snakeHead[] = L""; 
 23 wchar_t snakeBody[] = L""; 
 24 wchar_t snakeFood[] = L"";
 25 wchar_t mapWall[] = L"";
 26 
 27 wchar_t space[] = L" ";
 28  
 29 enum PlayKey_m {
 30     UP = 0,
 31     DOWN,
 32     LEFT = 3,
 33     RIGHT,
 34     SPACE,
 35     NON,
 36 };
 37 enum Statue_m {
 38     READY,
 39     START,
 40     RUNNING,
 41     STOP,
 42     PUDDING,
 43     EXIT,
 44 };
 45 struct Point_t {
 46     int x;
 47     int y;
 48 };
 49 
 50 class Game 
 51 {    
 52 public:
 53     Game(void) {                                          
 54         cout << "Game 构造执行了!" << endl;
 55     }
 56     ~Game(void) {
 57         cout << "Game 析构执行了!" << endl;
 58     }
 59     void gameInit(void) {
 60         for (int i = 0; i < DISPLAY_LEN; i++) {
 61             memset(&displayMap[i], ' ', sizeof(displayMap[0]));
 62         }
 63     }
 64     int getWherex(void);    // 获取光标x
 65     int getWherey(void);    // 获取光标y
 66     void setGotoxy(int x, int y) const;  // 设置光标位置
 67     wchar_t displayMap[DISPLAY_LEN][DISPLAY_LEN];  // 显示区域显存
 68 };
 69 
 70 class Snake : public Game
 71 {
 72 public:
 73     int speed;   // 速度
 74     int grade;     // 得分
 75     Statue_m statue;  
 76 
 77     Snake(void) {        
 78         statue = READY;
 79         cout << "Snake 构造执行了!" << endl;
 80     }
 81     ~Snake(void) {
 82         cout << "Snake 析构执行了!" << endl;
 83     }
 84     void init(void);
 85     void mapShow(void) ;      // 显示游戏版图
 86     void startGame(void) ;      // 开始游戏
 87 //    void settingGame(void) ; 
 88     void snakeRunning(void);
 89     PlayKey_m getPlayKey(void);
 90     void msg(const wchar_t *msg = L"欢迎", unsigned int x = MSG_X, unsigned int y = MSG_Y) const;  // 实时消息
 91     void exitGame(void) const; 
 92 private:
 93     unsigned int headdex;
 94     unsigned int taildex;
 95     PlayKey_m orientation;
 96     Point_t randomPoint;
 97     Point_t snakeSize[SNAKE_SIZE];
 98     void productFood(void);   // 产生蛇的食物
 99     void paintPoint(unsigned int x, unsigned int y, wchar_t * ch); // 刷新蛇区间
100     bool isExit(void);
101 };
102 /*
103  * 对游戏进行初始话,设置各初始状态
104 */
105 void Snake::init(void)
106 {
107     gameInit();
108     speed = 250;
109     grade = 2;
110     orientation = RIGHT;
111     headdex = 1;
112     taildex = 0;
113     statue = READY;
114     randomPoint.x = 0;
115     randomPoint.y = 0;
116     for (int i = 0; i < DISPLAY_LEN; i++) {
117         for (int j = 0; j < DISPLAY_LEN; j++) {
118             displayMap[i][j] = L' ';
119         }
120     }
121     snakeSize[headdex].x = 12;
122     snakeSize[headdex].y = 10;
123     snakeSize[taildex].x = 11;
124     snakeSize[taildex].y = 10;
125 }
126 /*
127  * 在游戏区域显示一个字符(宽字符形式)
128 */
129 void Snake::paintPoint(unsigned int x, unsigned int y, wchar_t * ch)
130 {
131     if (x >= DISPLAY_LEN || y >= DISPLAY_LEN) {
132         return;
133     }
134     displayMap[x][y] = *ch;
135     setGotoxy(x * 2 + 1, y + 1);
136     wprintf(L"%c", *ch);
137 }
138 /* 判断游戏是否结束 false:结束,true:未结束*/
139 bool Snake::isExit(void)
140 {
141     wchar_t content = displayMap[snakeSize[headdex].x][snakeSize[headdex].y];
142     if (content == *mapWall || content == *snakeBody) {
143         return false;
144     } else {
145         return true;
146     }
147 }
148 /*
149  * 随机产生蛇的食物
150 */
151 void Snake::productFood(void)
152 {
153     srand(time(0));
154     wchar_t ch;
155     do {
156         randomPoint.x = rand() % (DISPLAY_LEN - 3) + 1;
157         randomPoint.y = rand() % (DISPLAY_LEN - 3) + 1;
158         ch = displayMap[randomPoint.x][randomPoint.y];
159     } while(ch == *snakeBody || ch == *snakeHead || ch == *snakeFood); 
160 
161     paintPoint(randomPoint.x, randomPoint.y, snakeFood);
162 }
163 
164 void Snake::mapShow(void)
165 {
166     for (int i = 0; i < DISPLAY_LEN; i++) {
167         displayMap[0][i] = *mapWall;
168         displayMap[DISPLAY_LEN - 1][i] = *mapWall;
169         displayMap[i][0] = *mapWall;
170         displayMap[i][DISPLAY_LEN - 1] = *mapWall;
171     }
172     setGotoxy(1, 1);
173     for (int j = 0; j < DISPLAY_LEN; j++) {
174         for (int k = 0; k < DISPLAY_LEN; k++) {
175             wprintf(L"%c", displayMap[j][k]);
176         }
177         cout << endl;
178     }
179     setGotoxy(SPEED_X-9, SPEED_Y);
180     wcout << L"刷新数度:" ;
181     cout << speed << "ms";
182     setGotoxy(GRADE_X-9, GRADE_Y);
183     wcout << L"得    分:";
184     setGotoxy(STOP_REMIND_X-9, STOP_REMIND_Y);
185     wcout << L"暂停/继续: 空格";
186     setGotoxy(MSG_X-6, MSG_Y);
187     wcout << L"消息:";    
188 }
189 
190 void Snake::startGame(void)
191 {
192     paintPoint(snakeSize[headdex].x, snakeSize[headdex].y, snakeHead);
193     paintPoint(snakeSize[taildex].x, snakeSize[taildex].y, snakeBody);
194     productFood();
195     statue = START;
196 }
197 void Snake::msg(const wchar_t *msg, unsigned int x, unsigned int y) const
198 {
199     setGotoxy(x, y);
200     wcout << L"                  ";
201     setGotoxy(x, y);
202     wcout << msg;
203     setGotoxy(1, DISPLAY_LEN+1);
204 }
205 
206 void Snake::exitGame(void) const
207 {
208     msg(L"游戏已退出!");
209     exit(0);
210 }
211 /*
212  * 蛇进行运动
213 */
214 void Snake::snakeRunning(void)
215 {    
216     Point_t lastHead;
217     lastHead.x = snakeSize[headdex].x;
218     lastHead.y = snakeSize[headdex].y;
219     headdex = (headdex + 1) % SNAKE_SIZE;
220     switch(orientation) { // 新的蛇头
221         case UP:
222             snakeSize[headdex].x = lastHead.x;
223             snakeSize[headdex].y = lastHead.y - 1;
224             break;
225         case DOWN:
226             snakeSize[headdex].x = lastHead.x;
227             snakeSize[headdex].y = lastHead.y + 1;
228             break;
229         case LEFT:
230             snakeSize[headdex].x = lastHead.x - 1;
231             snakeSize[headdex].y = lastHead.y;
232             break;
233         case RIGHT:
234             snakeSize[headdex].x = lastHead.x + 1;
235             snakeSize[headdex].y = lastHead.y;
236             break;
237     }
238     if(!isExit()) {  // 判断是否结束
239         statue = PUDDING;    
240         return;
241     }
242     paintPoint(lastHead.x, lastHead.y, snakeBody);    // 添加蛇身
243     if (!(snakeSize[headdex].x == randomPoint.x &&
244           snakeSize[headdex].y == randomPoint.y)) {
245         paintPoint(snakeSize[taildex].x, snakeSize[taildex].y, L" "); // 去除蛇尾
246         snakeSize[taildex].x = 0,
247         snakeSize[taildex].y = 0;
248         taildex = (taildex + 1) % SNAKE_SIZE;
249     } else {
250         grade++;
251         productFood();    
252     }
253     paintPoint(snakeSize[headdex].x, snakeSize[headdex].y, snakeHead); // 新的蛇头
254     
255     setGotoxy(GRADE_X, GRADE_Y);
256     cout << grade;     // 刷新得分
257 }
258 
259 PlayKey_m Snake::getPlayKey(void)
260 {
261     unsigned char key;
262     static PlayKey_m lastKey = RIGHT;
263     PlayKey_m sult = NON;
264     if(!kbhit()) {
265         return NON;
266     }
267     lastKey = orientation;
268     key = getch();
269     if (key == 27) {  // 按下“Esc”键
270         statue = EXIT;
271     }
272     if (!(key == 0 || key == 224)) { // 字符键
273         if (key == ' ' && statue == RUNNING) {
274             statue = STOP;
275         } else if (key == ' ' && statue == STOP) {
276             statue = RUNNING;
277         }
278         return sult;
279     } else {   // 按下功能键
280         key = getch();
281         if (statue == STOP) {
282             return sult;
283         }
284     }
285     
286     switch(key) {    // 方向键
287         case 72: sult = UP; break;
288         case 80: sult = DOWN; break;
289         case 75: sult = LEFT; break;
290         case 77: sult = RIGHT; break;
291         default:
292             sult = NON;    break;
293     }    
294     
295     if (sult != NON) {
296         orientation = sult;
297         if (abs(sult - lastKey) == 1) {    // 蛇前进不能对折
298             orientation = lastKey;
299             sult = NON;
300         } 
301     }
302     return sult;
303 }
304 
305 int Game::getWherex()
306 {
307     CONSOLE_SCREEN_BUFFER_INFO pBuffer;
308     GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &pBuffer);
309     return (pBuffer.dwCursorPosition.X+1);
310 }
311 //获取光标的位置y
312 int Game::getWherey()
313 {
314     CONSOLE_SCREEN_BUFFER_INFO pBuffer;
315     GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &pBuffer);
316     return (pBuffer.dwCursorPosition.Y+1);
317 }
318 //设置光标的位置
319 void Game::setGotoxy(int x,int y) const
320 {
321     COORD c;
322     c.X=x-1;
323     c.Y=y-1;
324     SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),c);
325 }
326 
327 int main(void)
328 {
329     Snake snake1;
330     setlocale(LC_ALL, "chs"); // 设置语言为中文
331 
332     while(1) {
333         snake1.getPlayKey();   // 获取方向等键值
334         switch(snake1.statue) {
335             case READY:        // 游戏开始准备阶段
336                 snake1.init();
337                 snake1.mapShow();
338                 snake1.startGame();
339                 break;
340             case START:            // 开始阶段    
341                 snake1.msg(L"输入等级(1~9)");
342                 snake1.speed = (getch() - 48) * 100 ;
343                 if (snake1.speed < 100 || snake1.speed > 900) {
344                     snake1.speed = 250;
345                 } 
346                 snake1.setGotoxy(SPEED_X, SPEED_Y);
347                 cout << snake1.speed << "ms";
348                 snake1.statue = RUNNING;
349                 break;
350             case RUNNING:       // 运行阶段
351                 snake1.msg(L"正在运行...");
352                 snake1.snakeRunning();
353                 break;
354             case STOP:          
355                 snake1.msg(L"游戏已暂停");
356                 break;
357             case PUDDING:     // 本场游戏阶段
358                 snake1.msg(L"游戏已结束,按任意键重新开始");
359                 if (getch() == 27) {
360                     snake1.statue = EXIT;
361                 }
362                 snake1.statue = READY;
363                 system("cls");
364                 break;
365             case EXIT:  // 退出游戏
366                 snake1.exitGame();
367                 break;
368         }
369         Sleep(snake1.speed);
370     }
371     return 0;
372 }

猜你喜欢

转载自www.cnblogs.com/alex-space/p/13166433.html