引言:
就是练手用的,这个真没什么好说的了。
代码:
#include <stdio.h>
#include <windows.h>
#include <time.h>
// 宏定义
#define MAP_WIDTH 60 // 地图宽度
#define MAP_HEIGHT 30 // 地图高度
#define BRICK "□" // 四周的墙
#define GRID "■" // 内部的网格
#define SNAKE_MAX_SIZE 100 // 蛇最大节数
#define SNAKE_INIT_SIZE 3 // 蛇初始化节数
#define SNAKE_HEAD "★" // 蛇头
#define SNAKE_BODY "⊙" // 蛇身体
#define FOOD "◆" // 食物
// 方向枚举
enum _direction
{
DIR_UP, //上
DIR_DOWN, //下
DIR_LEFT, //左
DIR_RIGHT //右
};
// 游戏状态枚举
enum _status
{
RUNNING,
GAMEOVER,
EXIT
};
// 蛇结构体(未用链表)
struct _snake
{
int size; // 实际大小
enum _direction direction; // 运动方向
enum _status status; // 游戏状态
POINT nodePos[SNAKE_MAX_SIZE]; // 蛇各节点坐标位置
} snake;
// 食物结构体
struct _food
{
int x;
int y;
} food;
// 公用变量
int gTopScore = 0; // 最高分数
int gMyScore = 0; // 我的分数
int gSpeed = 300; // 原始速度
int gLevelSpeed = 300; // 第一关速度(每吃10个增加一次速度)
int gCurSpeed = 300; // 当前速度
int gMaxSpeed = 50; // 最大速度
// 函数定义
void hideCursor();
void setCursorPosition(int x,int y);
void setColor(int color);
void wecome();
void drawScore();
void drawTip();
void init();
int getTopScore();
void setTopScore(int score);
void gameStart();
void drawMap();
void drawSnake();
void drawFood();
void keyPress();
void moveSnake();
int collide();
void pause();
void gameOver();
void eatFood();
int main()
{
// 隐藏光标
hideCursor();
// 欢迎界面
wecome();
// 游戏开始
gameStart();
return 0;
}
// 隐藏光标显示
void hideCursor()
{
// 得到标准的输入(STD_INPUT_HANDLE)、输出(STD_OUTPUT_HANDLE)或错误(STD_ERROR_HANDLE)的设备的句柄
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
// 光标信息变量(typedef struct _CONSOLE_CURSOR_INFO {DWORD dwSize;WINBOOL bVisible;} CONSOLE_CURSOR_INFO)
CONSOLE_CURSOR_INFO cursorInfo;
// 获取当前光标信息
GetConsoleCursorInfo(hOut, &cursorInfo);
// 设置当前光标不可见
cursorInfo.bVisible = 0;
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursorInfo);
}
// 设置光标所在位置(x,y)
void setCursorPosition(int x,int y)
{
// 文字在控制台的坐标结构变量
COORD pos = {x,y};
// 设置控制台光标所在位置
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}
// 设置字体颜色
void setColor(int color)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),color);
}
// 显示欢迎界面
void wecome()
{
// 设置窗口标题
SetConsoleTitle("贪吃蛇");
// 设置窗口大小(30*100)
system("mode con cols=95 lines=30");
// 设置操作说明
setCursorPosition(37, 10);
printf("欢迎来到贪吃蛇的世界");
setCursorPosition(30, 14);
printf("提示:↑ ↓ ← → 控制贪吃蛇方向");
setCursorPosition(27, 16);
printf(" 空格暂停或继续游戏,ESC退出游戏");
setCursorPosition(37, 20);
system("pause");
system("cls");
}
// 从文件中读取最高分
int getTopScore()
{
int score = 0;
// 打开文件
FILE *pFile = fopen("./score.dat","rb");
// 读取得分
fread(&score,sizeof(int),1,pFile);
// 关闭文件
fclose(pFile);
return score;
}
// 将最高记录保存到文件中
void setTopScore(int score)
{
// 打开文件
FILE *pFile = fopen("./score.dat","wb");
// 写入得分
fwrite(&score,sizeof(int),1,pFile);
// 关闭文件
fclose(pFile);
}
// 游戏初始化
void init()
{
// 蛇初始化
snake.size = SNAKE_INIT_SIZE;
snake.direction = DIR_LEFT;
snake.status = RUNNING;
// 速度初始化
gSpeed = 300;
gLevelSpeed = 300;
gCurSpeed = 300;
gMaxSpeed = 50;
// 得分初始化
gTopScore = getTopScore();
gMyScore = 0;
}
// 游戏开始
void gameStart()
{
// 白底黑字
setColor(15);
// 初始化参数
init();
// 画地图
drawMap();
// 画蛇
drawSnake();
// 画随机生成的食物
drawFood();
// 游戏运行中
while(snake.status == RUNNING)
{
// 键盘事件
keyPress();
// 蛇移动
moveSnake();
// 吃食物
eatFood();
// 更新分数
drawScore();
// 休眠
Sleep(gCurSpeed);
}
// 游戏结束但还未确定是否退出
while(snake.status != EXIT)
{
Sleep(300);
gameOver();
}
}
// 画地图(注意墙是长=2宽=1的长方形字符)
void drawMap()
{
// 清屏
system("cls");
setColor(2);
// 画内部网格
for (int i = 0; i < MAP_WIDTH; i+=2)
{
for (int j = 0; j < MAP_HEIGHT; j++)
{
setCursorPosition(i, j);
printf(GRID);
}
}
setColor(6);
// 画左右墙
for (int i = 0; i < MAP_HEIGHT; i++)
{
setCursorPosition(0,i);
printf(BRICK);
setCursorPosition(MAP_WIDTH - 2,i); // 因为画上下边墙时取不到MAP_WIDTH,从0开始嘛。因此这里得减二,长=2,宽=1的长方形
printf(BRICK);
}
// 画上下墙
for (int i = 0; i < MAP_WIDTH; i+=2)
{
setCursorPosition(i,0);
printf(BRICK);
setCursorPosition(i,MAP_HEIGHT - 1); // 因为画左右边墙时取不到MAP_HEIGHT,从0开始嘛。因此这里得减一
printf(BRICK);
}
// 画得分
drawScore();
// 画提示
drawTip();
}
// 画得分
void drawScore()
{
setColor(15);
setCursorPosition(MAP_WIDTH + 2,2);
printf("最高得分:%5d",gTopScore);
setCursorPosition(MAP_WIDTH + 2,4);
printf("我的得分:%5d",gMyScore);
setCursorPosition(MAP_WIDTH + 2,6);
printf("我的速度:%5d米/秒",(2 * gSpeed - gCurSpeed));
}
// 画游戏提示
void drawTip()
{
setColor(9);
setCursorPosition(MAP_WIDTH + 2,9);
printf(" 游戏提示");
setCursorPosition(MAP_WIDTH + 2,10);
printf("┌─────────────┐");
setCursorPosition(MAP_WIDTH + 2,11);
printf("│ │");
setCursorPosition(MAP_WIDTH + 2,12);
printf("│每个食物得分:1 分 │");
setCursorPosition(MAP_WIDTH + 2,13);
printf("│ │");
setCursorPosition(MAP_WIDTH + 2,14);
printf("│不能穿墙也不能咬到自己哦 │");
setCursorPosition(MAP_WIDTH + 2,15);
printf("│ │");
setCursorPosition(MAP_WIDTH + 2,16);
printf("│用↑ ↓ ← → 控制蛇移动 │");
setCursorPosition(MAP_WIDTH + 2,17);
printf("│ │");
setCursorPosition(MAP_WIDTH + 2,18);
printf("│同一方向连续点击可以加速 │");
setCursorPosition(MAP_WIDTH + 2,19);
printf("│ │");
setCursorPosition(MAP_WIDTH + 2,20);
printf("│按空格可以暂停或继续游戏 │");
setCursorPosition(MAP_WIDTH + 2,21);
printf("│ │");
setCursorPosition(MAP_WIDTH + 2,22);
printf("│按ESC键可以退出游戏 │");
setCursorPosition(MAP_WIDTH + 2,23);
printf("│ │");
setCursorPosition(MAP_WIDTH + 2,24);
printf("└─────────────┘");
setColor(6);
setCursorPosition(MAP_WIDTH + 2,26);
printf(" 作者:我是小木鱼(Lag)");
setCursorPosition(MAP_WIDTH + 2,28);
printf(" 2023.02.15");
}
// 画蛇
void drawSnake()
{
// 画蛇头
setColor(14);
snake.nodePos[0].x = (MAP_WIDTH / 2);
if (snake.nodePos[0].x % 2 != 0){snake.nodePos[0].x--;} // x坐标必须是偶数
snake.nodePos[0].y = MAP_HEIGHT / 2;
setCursorPosition(snake.nodePos[0].x,snake.nodePos[0].y);
printf(SNAKE_HEAD);
// 画蛇身
for (int i = 1; i < SNAKE_INIT_SIZE; i++)
{
snake.nodePos[i].x = snake.nodePos[i - 1].x + 2;
snake.nodePos[i].y = snake.nodePos[i - 1].y;
setCursorPosition(snake.nodePos[i].x,snake.nodePos[i].y);
printf(SNAKE_BODY);
}
}
// 画随机生成的食物
void drawFood()
{
// rand() % 10 + i => [0-10) + i
srand(time(NULL)); // 随机种子
food.x = (rand() % ((MAP_WIDTH - 4)/ 2) + 1) * 2; // 必须为偶数,否则蛇吃不到
food.y = rand() % (MAP_HEIGHT - 2) + 1;
// 判断是否与蛇重合
for (int i = 0; i < snake.size; i++)
{
if (food.x == snake.nodePos[i].x && food.y == snake.nodePos[i].y)
{
drawFood();
break;
}
}
setCursorPosition(food.x,food.y);
setColor(12);
printf(FOOD);
}
// 键盘事件
void keyPress()
{
// 判断键盘
if (GetAsyncKeyState(VK_LEFT)) // 左键
{
if (snake.direction != DIR_RIGHT) // 禁止调头
{
if (snake.direction == DIR_LEFT)
{
if (gCurSpeed > gMaxSpeed){gCurSpeed -= 10;} // 方向相同,临时加速
}
else
{
snake.direction = DIR_LEFT;
if (gCurSpeed != gLevelSpeed){gCurSpeed = gLevelSpeed;} // 方向不同,恢复原来速度
}
}
}
else if(GetAsyncKeyState(VK_UP)) // 上键
{
if (snake.direction != DIR_DOWN)
{
if (snake.direction == DIR_UP)
{
if (gCurSpeed > gMaxSpeed){gCurSpeed -= 10;} // 方向相同,临时加速
}
else
{
snake.direction = DIR_UP;
if (gCurSpeed != gLevelSpeed){gCurSpeed = gLevelSpeed;} // 方向不同,恢复原来速度
}
}
}
else if(GetAsyncKeyState(VK_RIGHT)) // 右键
{
if (snake.direction != DIR_LEFT)
{
if (snake.direction == DIR_RIGHT)
{
if (gCurSpeed > gMaxSpeed){gCurSpeed -= 10;} // 方向相同,临时加速
}
else
{
snake.direction = DIR_RIGHT;
if (gCurSpeed != gLevelSpeed){gCurSpeed = gLevelSpeed;} // 方向不同,恢复原来速度
}
}
}
else if(GetAsyncKeyState(VK_DOWN)) // 下键
{
if (snake.direction != DIR_UP)
{
if (snake.direction == DIR_DOWN)
{
if (gCurSpeed > gMaxSpeed){gCurSpeed -= 10;} // 方向相同,临时加速
}
else
{
snake.direction = DIR_DOWN;
if (gCurSpeed != gLevelSpeed){gCurSpeed = gLevelSpeed;} // 方向不同,恢复原来速度
}
}
}
else if(GetAsyncKeyState(VK_SPACE)) // 空格暂停
{
pause();
}
else if (GetAsyncKeyState(VK_ESCAPE)) // 退出
{
snake.status = GAMEOVER;
}
}
// 移动蛇
void moveSnake()
{
// 判断该方向是否可以移动(要发生碰撞)
if (collide() == 0){return;}
// 擦除最后一节
setCursorPosition(snake.nodePos[snake.size - 1].x,snake.nodePos[snake.size - 1].y);
setColor(2);
printf(GRID);
// 循环替换除蛇头外的其他节
for (int i = snake.size - 1; i > 0; i--)
{
snake.nodePos[i].x = snake.nodePos[i - 1].x;
snake.nodePos[i].y = snake.nodePos[i - 1].y;
setCursorPosition(snake.nodePos[i].x,snake.nodePos[i].y);
setColor(14);
printf(SNAKE_BODY);
}
// 处理蛇头
switch (snake.direction)
{
case DIR_UP:
snake.nodePos[0].y--;
break;
case DIR_DOWN:
snake.nodePos[0].y++;
break;
case DIR_LEFT:
snake.nodePos[0].x-=2;
break;
case DIR_RIGHT:
snake.nodePos[0].x+=2;
break;
}
setCursorPosition(snake.nodePos[0].x,snake.nodePos[0].y);
setColor(14);
printf(SNAKE_HEAD);
}
// 判断蛇移动的下一个位置能否发生碰撞
int collide()
{
int newX = snake.nodePos[0].x;
int newY = snake.nodePos[0].y;
switch (snake.direction)
{
case DIR_UP:
newY = snake.nodePos[0].y - 1;
break;
case DIR_DOWN:
newY = snake.nodePos[0].y + 1;
break;
case DIR_LEFT:
newX = snake.nodePos[0].x - 2;
break;
case DIR_RIGHT:
newX = snake.nodePos[0].x + 2;
break;
}
// 判断是否撞墙
if (newX < 2 || newX > MAP_WIDTH - 4 || newY < 1 || newY > MAP_HEIGHT - 2)
{
// 游戏失败
snake.status = GAMEOVER;
return 0;
}
// 判断是否撞自身
for (int i = 1; i < snake.size; i++)
{
if (newX == snake.nodePos[i].x && newY == snake.nodePos[i].y)
{
// 游戏失败
snake.status = GAMEOVER;
return 0;
}
}
return 1;
}
// 吃食物
void eatFood()
{
if (snake.nodePos[0].x == food.x && snake.nodePos[0].y == food.y)
{
// 蛇长一节
if (snake.size < SNAKE_MAX_SIZE)
{
snake.nodePos[snake.size].x = snake.nodePos[snake.size - 1].x;
snake.nodePos[snake.size].y = snake.nodePos[snake.size - 1].y;
setCursorPosition(snake.nodePos[snake.size].x,snake.nodePos[snake.size].y);
setColor(14);
printf(SNAKE_BODY);
snake.size++;
}
// 记录得分
gMyScore++;
// 判断速度是否加快
if (gMyScore % 10 == 0) // 10个为一关
{
if (gLevelSpeed > gMaxSpeed)
{
gLevelSpeed -= 50;
gCurSpeed = gLevelSpeed;
}
}
// 重新生成食物
drawFood();
}
}
// 游戏暂停
void pause()
{
while (1)
{
Sleep(300);
if (GetAsyncKeyState(VK_SPACE)){break;} // 再按一次空格继续游戏
}
}
// 游戏结束
void gameOver()
{
if (snake.status == RUNNING){return;}
// 记录得分
if (gMyScore > gTopScore){setTopScore(gMyScore);}
// 胜败乃兵家常事,大虾请重新来过吧!
setColor(136);
for (int i = 10; i <= MAP_WIDTH - 12; i+=2)
{
for (int j = 9; j <= MAP_HEIGHT - 11; j++)
{
setCursorPosition(i, j);
printf(GRID);
}
}
setColor(119);
for (int i = 12; i <= MAP_WIDTH - 14; i+=2)
{
for (int j = 10; j <= MAP_HEIGHT - 12; j++)
{
setCursorPosition(i, j);
printf(GRID);
}
}
setColor(116);
setCursorPosition(14, 13);
printf("胜败乃兵家常事,大虾请重新来过吧!");
setCursorPosition(14, 15);
printf(" 是否重新开始游戏(Y/N)?");
char key;
scanf("%c",&key);
if (key == 'y' || key == 'Y')
{
getchar(); // 吸收回车键
gameStart();
return;
}
else
{
getchar();
snake.status = EXIT;
setColor(0);
system("cls");
return;
}
}
下载:
这个就不需要了吧,代码就这么几行而已。