假期无事,整理电脑文件的时候发现了以前大二时做的坦克小游戏,心血来潮,决定再来一发贪吃蛇。
游戏玩法不必多说,主要是算法实现和绘制过程。
首先,利用一个二维数组 mp[][] 来存储地图信息,其中的值表示:
0:空
1:被蛇覆盖
2:食物
有了这个地图数组,生成随机食物的时候就可以避免生成到蛇身上。
那蛇的身体如何存储呢?也很简单,用队列(存储每一个小格的坐标信息)。
队列的头尾方向与蛇的头尾方向正好相反。
蛇每走一步,在蛇头方向的下一位置画一个小方格,同时把该位置放置到队列尾端。取出队列第一个元素,也即是蛇的尾部的坐标,把该位置的小方格清掉。
画一个小格,擦掉一个小格,再画一个,再擦一个……如此反复循环,小蛇就会爬了~
代码不多,写到一个文件里了:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <time.h> #include <conio.h> #include <windows.h> #include <queue> #ifdef _MSC_VER // M$的编译器要给予特殊照顾 #if _MSC_VER <= 1200 // VC6及以下版本 #error 你是不是还在用VC6?! #else // VC6以上版本 #if _MSC_VER >= 1600 // 据说VC10及以上版本有stdint.h了 #include <stdint.h> #else // VC10以下版本,自己定义int8_t和uint16_t typedef signed char int8_t; typedef unsigned short uint16_t; #endif #ifndef __cplusplus // 据说VC都没有stdbool.h,不用C++编译,自己定义bool typedef int bool; #define true 1 #define false 0 #endif #endif #else // 其他的编译器都好说 #include <stdint.h> #ifndef __cplusplus // 不用C++编译,需要stdbool.h里的bool #include <stdbool.h> #endif #endif using namespace std; HANDLE g_hConsoleOutput; // 控制台输出句柄 // 方向 enum { DIR_UP = 0, DIR_LEFT, DIR_DOWN, DIR_RIGHT }; // 绘画开始位置 const int START_ROW = 3; const int START_COL = 3; // 蛇的初始值 const int START_X = 5; const int START_Y = 6; const int START_LENGTH = 5; // 地图大小 const int WIDTH = 32; const int HEIGHT = 24; // 定位到游戏池中的方格 #define gotoxyInPool(x, y) gotoxyWithFullwidth(x+START_ROW, y+START_COL) int mp[HEIGHT][WIDTH]; typedef struct Point { int x, y; } Point; typedef struct Snake // 这个结构体存储游戏相关数据 { Point head; queue<Point>body; int dir; public : void setDir(); Point getHead(); int nextStep(); } Snake; // ============================================================================= typedef struct SnakeControl // 这个结构体存储控制相关数据 { // 游戏池内每格的颜色 // 由于此版本是彩色的,仅用游戏池数据无法存储颜色信息 // 当然,如果只实现单色版的,就没必要用这个数组了 int8_t color[28][16]; bool dead; // 挂 bool pause; // 暂停 unsigned score; // 得分(这个也可以用蛇的长度表示) } SnakeControl; // ============================================================================= // 以全角定位到某点 void gotoxyWithFullwidth(short y, short x); void initGame(Snake * snake, SnakeControl * control); void printSnake(Snake * snake, SnakeControl * control); void createFood(Snake * snake); void GoToNextStep(Snake * snake, SnakeControl * control, int dir); void keydownControl(Snake * snake, SnakeControl * control, int key); void runGame(Snake * snake, SnakeControl * control); void printPrompting(); void printPoolBorder(); void printScore(const Snake *snake, const SnakeControl *control); int main() { Snake snake; SnakeControl control; CONSOLE_CURSOR_INFO cursorInfo = { 1, FALSE }; // 光标信息 g_hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE); // 获取控制台输出句柄 SetConsoleCursorInfo(g_hConsoleOutput, &cursorInfo); // 设置光标隐藏 SetConsoleTitleA("贪吃蛇"); printPrompting(); while (1) { SetConsoleTextAttribute(g_hConsoleOutput, 0x07); system("cls"); initGame(&snake,&control); // 初始化游戏 printPrompting(); // 显示提示信息 printPoolBorder(); // 显示游戏池边界 runGame(&snake,&control); // 运行游戏 SetConsoleTextAttribute(g_hConsoleOutput, 0xF0); char * str = "Game Over"; int len = strlen(str); // 居中显示 int x = HEIGHT / 2 + START_ROW ; int y = WIDTH / 2 + START_COL - len/4; gotoxyWithFullwidth(x, y); printf("%s",str); SetConsoleTextAttribute(g_hConsoleOutput, 0x07); system("pause > nul"); system("cls"); } gotoxyWithFullwidth(0, 0); CloseHandle(g_hConsoleOutput); system("pause > nul"); return 0; } // ============================================================================= // 以全角定位到某点 void gotoxyWithFullwidth(short y, short x) { static COORD cd; cd.X = (short)(x << 1); cd.Y = y; SetConsoleCursorPosition(g_hConsoleOutput, cd); } void initGame(Snake * snake, SnakeControl * control) { //清空数据 memset(mp, 0, sizeof mp); while (!snake->body.empty()) snake->body.pop(); //出生点 Point next; next.x = START_X ; next.y = START_Y ; snake->dir = DIR_RIGHT; snake->head.x = next.x; snake->head.y = next.y; for (int i = 0; i < START_LENGTH; ++i)snake->body.push(next); //生成食物 createFood(snake); //控制信息 control->dead = false; control->pause = false; control->score = 0; } //生成随机食物 void createFood(Snake * snake) { int x = 0, y = 0; do { x = rand() % HEIGHT; y = rand() % WIDTH; } while (mp[x][y] == 1); // 不能生成到蛇的身体上面 mp[x][y] = 2; gotoxyInPool(x, y); // 用相应颜色,显示一个实心方块 SetConsoleTextAttribute(g_hConsoleOutput, 0xA); printf("■"); } void GoToNextStep(Snake * snake, SnakeControl * control, int dir) { int x = snake->head.x; int y = snake->head.y; Point next; switch (dir) { case DIR_UP: next.x = x - 1; next.y = y; break; case DIR_DOWN: next.x = x + 1; next.y = y; break; case DIR_LEFT: next.x = x; next.y = y - 1; break; case DIR_RIGHT: next.x = x; next.y = y + 1; break; default: break; } if (next.x >= 0 && next.x < HEIGHT && next.y >= 0 && next.y < WIDTH ) { if (mp[next.x][next.y] == 1) { control->dead = true; return; } bool isEat = mp[next.x][next.y] == 2; Point last = snake->body.front(); snake->head.x = next.x; snake->head.y = next.y; snake->body.push(next); snake->dir = dir; // 画下一个头的位置 mp[next.x][next.y] = 1; gotoxyInPool(next.x , next.y ); SetConsoleTextAttribute(g_hConsoleOutput, 0xF); printf("■"); // 如果没有吃到食物,删掉尾巴的痕迹 if (!isEat) { snake->body.pop(); mp[last.x][last.y] = 0; gotoxyInPool(last.x , last.y ); SetConsoleTextAttribute(g_hConsoleOutput, 0xA); printf("%2s", ""); } else { createFood(snake); printScore(snake, control); } } else { control->dead = true; } } void keydownControl(Snake * snake, SnakeControl * control, int key) { if (key == 13) // 暂停/解除暂停 { control->pause = !control->pause; if (control->pause) { SetConsoleTextAttribute(g_hConsoleOutput, 0xE); gotoxyWithFullwidth(5, 38); printf(" 已暂停 "); return; } else { //清楚提示信息 SetConsoleTextAttribute(g_hConsoleOutput, 0xE); gotoxyWithFullwidth(5, 38); printf("%18s",""); //显示得分信息 printScore(snake, control); } } if (control->pause) // 暂停状态,不作处理 { return; } int dir = 0; switch (key) { case ‘w‘: case ‘W‘: case ‘8‘: case 72: // 上 dir = DIR_UP; break; case ‘a‘: case ‘A‘: case ‘4‘: case 75: // 左 dir = DIR_LEFT; break; case ‘d‘: case ‘D‘: case ‘6‘: case 77: // 右 dir = DIR_RIGHT; break; case ‘s‘: case ‘S‘: case ‘2‘: case 80: // 下 dir = DIR_DOWN; break; default: return; } // 不允许掉头 if (snake->dir != dir && (snake->dir + dir) % 2 == 0) { return; } snake->dir = dir; } void runGame(Snake * snake, SnakeControl * control) { int ch; clock_t clockLast, clockNow; clockLast = clock(); // 计时 printScore(snake, control); // 显示游戏池 while (!control->dead) // 没挂 { while (_kbhit()) // 有键按下 { ch = _getch(); if (ch == 27) // Esc键 { return; } keydownControl(snake, control, ch); // 处理按键 } if (!control->pause) // 未暂停 { clockNow = clock(); // 计时 // 两次记时的间隔超过0.45秒 if (clockNow - clockLast > 0.15F * CLOCKS_PER_SEC) { clockLast = clockNow; GoToNextStep(snake, control, snake->dir);; // 蛇自动往前走 } } } } void printPrompting() { // 边框 SetConsoleTextAttribute(g_hConsoleOutput, 0xF); gotoxyWithFullwidth(3, 37); printf("┏━━━━━━━━━┓"); gotoxyWithFullwidth(4, 37); printf("┃%18s┃", "", ""); gotoxyWithFullwidth(5, 37); printf("┃%18s┃", "", ""); gotoxyWithFullwidth(6, 37); printf("┃%18s┃", "", ""); gotoxyWithFullwidth(7, 37); printf("┗━━━━━━━━━┛"); SetConsoleTextAttribute(g_hConsoleOutput, 0xB); gotoxyWithFullwidth(10, 37); printf("■控制说明:"); gotoxyWithFullwidth(12, 38); printf("□向左移动:← A 4"); gotoxyWithFullwidth(13, 38); printf("□向右移动:→ D 6"); gotoxyWithFullwidth(14, 38); printf("□向下移动:↓ S 2"); gotoxyWithFullwidth(15, 38); printf("□向上移动:↑ W 8"); gotoxyWithFullwidth(26, 37); printf("■By: woffee 17.01.20"); } // ============================================================================= // 显示得分信息 void printScore(const Snake *snake, const SnakeControl *control) { SetConsoleTextAttribute(g_hConsoleOutput, 0xE); gotoxyWithFullwidth(5, 41); printf("得分:%d", snake->body.size()); } // ============================================================================= // 显示游戏边界 void printPoolBorder() { int x, y; SetConsoleTextAttribute(g_hConsoleOutput, 0xF0); x = START_ROW-1, y = START_COL-1; for (int i = 0; i <= WIDTH ; ++i) { gotoxyWithFullwidth(x, y); printf("%2s", ""); y++; } x = START_ROW + HEIGHT, y = START_COL; for (int i = 0; i <= WIDTH ; ++i) { gotoxyWithFullwidth(x, y); printf("%2s", ""); y++; } x = START_ROW, y = START_COL-1; for (int i = 0; i <= HEIGHT; ++i) { gotoxyWithFullwidth(x, y); printf("%2s", ""); x++; } x = START_ROW-1, y = START_COL + WIDTH; for (int i = 0; i <= HEIGHT; ++i) { gotoxyWithFullwidth(x, y); printf("%2s", ""); x++; } }
时间: 2024-10-06 19:29:57