风惜雨 - 云代码空间
——
#include <Windows.h> #include <stdlib.h> #include <stdio.h> #include <conio.h> #include <time.h> /// 游戏活动区域 #define GAME_W 20 #define GAME_H 14 /// 方向 #define UP 72 #define DOWN 80 #define LEFT 75 #define RIGHT 77 #define gotoxyInGameAndPrint(x, y, color, ch) gotoxyAndPrint(x + 1, y + 1, color, ch) /// 蛇结点 typedef struct { int x; int y; }Node; /// 游戏相关数据 typedef struct { int map [GAME_H][GAME_W]; // 二维数组,用于碰撞检测 Node snake [GAME_H * GAME_W]; // 蛇身 double speed; // 速度 int length; // 蛇身长度 int director; // 蛇头当前方向 Node food; // 食物(坐标) bool pause; // 游戏暂停 bool dead; // 是否死亡 }Data; /// 句柄 HANDLE g_hConsoleHandl; Data gameData; //------------------------------------------------------------------- // // 函数声明 // //------------------------------------------------------------------- void gotoxyAndPrint (int x, int y, unsigned color, char* ch); // 设置光标位置并打印 void onEnter (); // 游戏开始前的处理 void gameMenu (); // 游戏菜单 void lostGame (); // 游戏失败 void drawGameBorder (int w, int h); // 绘制游戏边界 void printPrompting (); // 显示提示信息 void printGameData (Data* data); // 显示游戏数据 void initGame (Data* data); // 初始化游戏数据 void runGame (Data* data); // 运行游戏 void keyDownControl (Data* data, char key); // 键盘按键控制 Node getNewPos (Data* data); void move (Data* data); // 移动蛇 void updateMap (Data* data); // 更新 map void repaint (Data* data); // 刷新画面 void createFood (Data* data); // 生成食物 bool isEatDood (Data* data); // 判断前方是否有食物可吃 void eatFood (Data* data); // 吃食物 bool isDead (Data* data); // 是否死亡(结束游戏) //------------------------------------------------------------------- // // 主函数 main() // //------------------------------------------------------------------- int main () { onEnter (); while ( TRUE ) { gameMenu (); initGame (&gameData); runGame (&gameData); lostGame (); } system ("pause"); return 0; } // // 游戏开始前的处理 // void onEnter () { g_hConsoleHandl = GetStdHandle (STD_OUTPUT_HANDLE); // 获取标准输出句柄 CONSOLE_CURSOR_INFO cursorInfo = { 1, FALSE }; // 光标信息 SetConsoleCursorInfo (g_hConsoleHandl, &cursorInfo); // 隐藏光标 system ("title GameSnake"); // 设置控制台窗口标题 } // // 设置光标位置 // void gotoxyAndPrint (int x, int y, unsigned color, char* ch) { COORD pos; pos.X = x << 1; pos.Y = y; // 移动到目标 SetConsoleTextAttribute (g_hConsoleHandl, color); // 设置颜色 SetConsoleCursorPosition (g_hConsoleHandl, pos); printf ("%s", ch); } // // 游戏菜单 // void gameMenu () { gotoxyAndPrint (15, 5, 0x0F, "┏━━━━━┓"); gotoxyAndPrint (15, 6, 0x0F, ""); printf ("┃ %s ┃", "贪食蛇"); gotoxyAndPrint (15, 7, 0x0F, "┗━━━━━┛"); gotoxyAndPrint (14, 10, 0x0F, " 按任意键开始游戏 "); while ( _getch () ) { system ("cls"); break; } } // // 游戏失败 // void lostGame () { gotoxyAndPrint (6, 6, 0x7, "死亡!!按任意键返回"); // pause 会显示 按任意键退出 // pause>nul 就是将显示的内容重定向到nul 就是 不显示按任意键退出 system ("pause > nul"); system ("cls"); // 清屏 } // // 是否死亡(结束游戏) // bool isDead (Data* data) { Node pos = getNewPos (data); return ( pos.x < 0 || pos.x > GAME_W - 1 || pos.y < 0 || pos.y > GAME_H - 1 || data->map [pos.y][pos.x] ); } // // 初始化游戏数据 // void initGame (Data* data) { // 设置随机种子 srand (( unsigned int ) time (NULL)); // 将 dot(游戏数据) 和 node(蛇身) 全部置0 memset (data, 0, sizeof( Data )); memset (data->snake, 0, sizeof( Node ) * 50); // 初始化数据 data->length = 1; // 蛇长度为 1 data->director = RIGHT; // 开始时方向向右 data->dead = FALSE; // 不暂停 data->speed = 0.3; // 速度 // 创建蛇身 data->snake [1].x = rand () % ( GAME_W - 5 ); data->snake [1].y = rand () % GAME_H; data->snake [0] = data->snake [1]; // 生成食物 createFood (data); drawGameBorder (GAME_W, GAME_H); // 绘制边界 printPrompting (); // 显示提示信息 printGameData (&gameData); // 显示游戏数据 } // // 绘制游戏边界 // void drawGameBorder (int w, int h) { int x, y; // 打印上下边界 for ( x = 0; x < w + 1; x++ ) { gotoxyAndPrint (x, 0, 0x7, "■"); gotoxyAndPrint (x, h + 1, 0x7, "■"); } // 打印左右边界 for ( y = 0; y < h + 2; y++ ) { gotoxyAndPrint (0, y, 0x7, "■"); gotoxyAndPrint (w + 1, y, 0x7, "■"); } } // // 显示提示信息 // void printPrompting () { gotoxyAndPrint (GAME_W + 4, 1, 0x9, "■游戏提示:"); gotoxyAndPrint (GAME_W + 4, 3, 0x9, "□向下移动:↓"); gotoxyAndPrint (GAME_W + 4, 4, 0x9, "□向上移动:↑"); gotoxyAndPrint (GAME_W + 4, 5, 0x9, "□向左移动:←"); gotoxyAndPrint (GAME_W + 4, 6, 0x9, "□向右移动:→"); gotoxyAndPrint (GAME_W + 4, 7, 0x9, "□暂停游戏:回车"); gotoxyAndPrint (GAME_W + 4, 8, 0x9, "□提升速度:空格"); } // // 显示游戏数据 // void printGameData (Data* data) { int startY = 10; // 垂直方向的起始坐标 // GAME_W + 4 : 右边界在空 2 格 gotoxyAndPrint (GAME_W + 4, startY, 0xD, "■游戏数据:"); gotoxyAndPrint (GAME_W + 4, startY + 2, 0xD, ""); printf ("□移动速度: %.2lf", data->speed); gotoxyAndPrint (GAME_W + 4, startY + 3, 0xD, ""); printf ("□蛇的长度: %d", data->length); gotoxyAndPrint (GAME_W + 4, startY + 4, 0xD, ""); printf ("□食物位置:( %2d - %2d ) ", data->food.x, data->food.y); gotoxyAndPrint (GAME_W + 4, startY + 5, 0xD, ""); printf ("□蛇头位置:( %2d - %2d ) ", data->snake [data->length].x, data->snake [data->length].y); } // // 根据蛇头的方向,获取蛇头下一个位置的坐标 // Node getNewPos (Data* data) { // 获取蛇头的坐标 Node newPos = data->snake [data->length]; // 根据蛇头的方向,获取蛇头下一个位置的坐标 switch ( data->director ) { case UP: newPos.y--; break; case DOWN: newPos.y++; break; case LEFT: newPos.x--; break; case RIGHT: newPos.x++; break; } return newPos; } // // 移动蛇 // void move (Data* data) { // 判断前方是否有食物,如果有食物,先吃掉食物,再生成新的食物 if ( isEatDood (data) ) { eatFood (data); // 吃掉食物 createFood (data); // 生成新的食物 return; } // 判断是否死亡(即碰到边界 或 自己) if ( isDead (data) ) { data->dead = TRUE; // 标记为死亡,退出游戏循环 return; } // 实现蛇的移动 memmove (data->snake, data->snake + 1, sizeof( data->snake [0] ) * data->length); data->snake [data->length] = getNewPos (data); updateMap (data); // 更新 map repaint (data); // 重绘蛇 printGameData (data); // 刷新游戏数据 } // // 刷新画面 // void repaint (Data* data) { // 先擦除尾巴 gotoxyInGameAndPrint (data->snake [0].x, data->snake [0].y, 0xC, " "); // 新显示蛇头 gotoxyInGameAndPrint (data->snake [data->length].x, data->snake [data->length].y, 0xC, "■"); } // // 更新 map // void updateMap (Data* data) { data->map [data->snake [data->length].y][data->snake [data->length].x] = 1; data->map [data->snake [0].y][data->snake [0].x] = 0; } // // 生成食物 // void createFood (Data* data) { // 获取随机坐标 int x = 0, y = 0; do { x = rand () % GAME_W; y = rand () % GAME_H; } while ( data->map [y][x] ); // 设置食物坐标 data->food.x = x; data->food.y = y; // 绘制食物 gotoxyInGameAndPrint (data->food.x, data->food.y, 0xA, "■"); } // // 判断前方是否有食物可吃 // bool isEatDood (Data* data) { Node pos = getNewPos (data); // 获取蛇头的下一个位置坐标 // 判断是否和食物坐标重合 return ( pos.x == data->food.x && pos.y == data->food.y ); } // // 吃食物 // void eatFood (Data* data) { // 增加蛇身长度 data->snake [data->length + 1] = getNewPos (data); data->length++; // 蛇长度 + 1 updateMap (data); // 更新 map // 绘制新的节点 gotoxyInGameAndPrint (data->snake [data->length].x, data->snake [data->length].y, 0xC, "■"); printGameData (data); // 刷新游戏数据 } // // 运行游戏 // void runGame (Data* data) { char key; clock_t clockLast, clockNow; clockLast = clock (); // 获取第一次计时 while ( !data->dead ) { // 获取键盘输入 while ( _kbhit () ) { key = _getch (); // TODO: 相关键盘控制代码 keyDownControl (data, key); } // 暂停 if ( data->pause ) { continue; } clockNow = clock (); // 获取当前计时 // 如果时间间隔 > speed if ( clockNow - clockLast > data->speed * CLOCKS_PER_SEC ) { clockLast = clockNow; // TODO: 相关游戏逻辑代码 move (data); } } } // // 键盘控制处理 // void keyDownControl (Data* data, char key) { // 暂停 if ( key == 13 ) { data->pause = !data->pause; } // 不处理 if ( data->pause ) { return; } switch ( key ) { case 72: /* 上 */ if ( data->director != DOWN ) { data->director = UP; } break; case 80: /* 下 */ if ( data->director != UP ) { data->director = DOWN; } break; case 75: /* 左 */ if ( data->director != RIGHT ) { data->director = LEFT; } break; case 77: /* 右 */ if ( data->director != LEFT ) { data->director = RIGHT; } break; case ' ': data->speed -= 0.05; break; } }