使用c++,编写了个贪吃蛇游戏,先看效果图如下:
程序实现速度等级选择、得分显示、中途暂停、重新开始等功能。
平台:win32控制平台,VC6.0。
实现过程:
1)、创建两个类:游戏类和蛇类。
2)、游戏类实现游戏版图空间和版图操作坐标功能,蛇类丰富版图显示并提供游戏进行的操作方法。
3)、蛇类中具备版图构建、开始游戏、退出游戏、创建蛇躯、蛇运动、随机生产食物、实时消息显示等方法。
4)、游戏运行时,系统分为准备、开始、运行、暂停、挂起、退出等状态,根据状态不同执行不同操作。
准备:初始化游戏状态,构建游戏显示的版图,具备初始蛇身。
开始:输入具体数度等级(1~9级)后开始。
运行:蛇吃食物,并随机产生食物。
暂停:空格键可以暂停/启动游戏。
挂起:当游戏结束时,处于挂起状态,任意键重新开始,即回到准备态。
退出:结束程序。
操作要点:
1)、由于使用char型字符显示蛇身横竖间距不一致,影响美观,构建版图时使用宽字符创建。即定义wchar_t型的空间。wchar_t数据需要使用std::wcout或wprintf()函数输出,并且输出时还要设置本地语言,使用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 }