目录
源码展示
#include <stdio.h>
#include <Windows.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>
#define SNAKE_HEAD ●//蛇头
#define SNAKE_BODY ○//蛇身
#define initSIZE 3//初始化蛇长
#define MAP_HEAD 30//初始化地图高
#define MAP_WIDETH 80//初始化地图宽
#define SNAKE_MAXLENGTH 100//蛇的最大长度
int flag = 1;
int dir = 'a';
int score = 0;
int speed = 250;
struct Snake//蛇的坐标
{
int x[SNAKE_MAXLENGTH];
int y[SNAKE_MAXLENGTH];
int length;
}snake;
struct Food//食物坐标
{
int food_x;
int food_y;
}food;
void color(int c);
void gotoxy(int x, int y);//控制光标
void HideCursor();//隐藏光标
void MAP();
void beginGame();
void snake_ADDRESS();
void snake_FOOD();
void snake_MOVE();
int snake_DEATH();
void snake_SCORE();
int main()
{
MAP();
beginGame();
MAP();
snake_ADDRESS();
while (1)
{
snake_SCORE();
HideCursor();
if (flag)
snake_FOOD();
Sleep(speed);
if (snake_DEATH())
{
gotoxy(MAP_WIDETH / 2, MAP_HEAD / 2);
printf("游戏结束,您的最终得分为:%d\n", score);
gotoxy(MAP_WIDETH / 2, MAP_HEAD / 2 + 2);
color(3);
system("pause");
gotoxy(95, 29);//移除光标
return 0;
}
snake_MOVE();
}
return 0;
}
void gotoxy(int x, int y)
{
COORD pos = { x,y };
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);// 获取标准输出设备句柄
SetConsoleCursorPosition(hOut, pos);//两个参数分别是指定哪个窗体,具体位置
}
void HideCursor() // 用于隐藏光标
{
CONSOLE_CURSOR_INFO cursor_info = { 1, 0 }; // 第二个值为0表示隐藏光标
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);//函数和结构体都在windows.h中定义。
}
void color(int c)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); //颜色设置
}
//打印地图
void MAP()
{
color(4);
for (int i = 1; i < MAP_WIDETH; i += 2) //地图宽80,高30
{
gotoxy(i, 0);//上边界
printf("■");
gotoxy(i, MAP_HEAD - 1);//下边界
printf("■");
}
for (int i = 1; i < MAP_HEAD; i++)
{
gotoxy(0, i);//左边界
printf("■");
gotoxy(MAP_WIDETH, i);//右边界
printf("■");
}
gotoxy(0, 0); //移动光标
}
//打印蛇
void snake_ADDRESS()
{
snake.x[0] = MAP_WIDETH / 2;
snake.y[0] = MAP_HEAD / 2;
snake.length = initSIZE;
color(7);
//打印蛇头
gotoxy(snake.x[0], snake.y[0]);
printf("◎");
//循环打印蛇身
color(14);
for (int i = 1; i < snake.length; i++)
{
snake.x[i] = snake.x[i - 1] + 2;
snake.y[i] = snake.y[i - 1];
gotoxy(snake.x[i], snake.y[i]);
printf("○");
}
gotoxy(70, 0);//移除光标
}
//打印食物
void snake_FOOD()
{
color(6);
srand(time(NULL));
//确保食物产生的位置的x坐标为偶数
here:while (1)
{
food.food_x = rand() % 76 + 2;
if (food.food_x % 2 != 0)
continue;
else
break;
}
food.food_y = rand() % 26 + 2;
//使食物不打印在蛇身上
for (int i = 1; i < snake.length; i++)
{
if (food.food_x == snake.x[i] && food.food_y == snake.y[i])
goto here;
}
gotoxy(food.food_x, food.food_y);
printf("■");
flag = 0;
gotoxy(70, 0);//移除光标
}//移动蛇
void snake_MOVE()
{
int p;//记录输入的方向
color(10);
if (_kbhit())//监听是否有键盘输入
{
fflush(stdin);
p = _getch();
//防止蛇反方向移动
if (dir == 'w' || dir == 'W')
{
if (p != 's' && p != 'S')
dir = p;
}
if (dir == 's' || dir == 'S')
{
if (p != 'w' && p != 'W')
dir = p;
}
if (dir == 'a' || dir == 'A')
{
if (p != 'd' && p != 'D')
dir = p;
}
if (dir == 'd' || dir == 'D')
{
if (p != 'a' && p != 'A')
dir = p;
}
}
//擦除最后一节蛇身
gotoxy(snake.x[snake.length - 1], snake.y[snake.length - 1]);
printf(" ");
gotoxy(95, 0);//移除光标
//移动蛇
for (int i = snake.length - 1; i > 0; i--)
{
snake.x[i] = snake.x[i - 1];
snake.y[i] = snake.y[i - 1];
gotoxy(snake.x[i], snake.y[i]);
printf("○");
}
//通过按键改变蛇的方向
switch (dir)
{
case 'w':
case 'W':
snake.y[0]--;
break;
case 's':
case 'S':
snake.y[0]++;
break;
case 'a':
case 'A':
snake.x[0] -= 2;
break;
case 'd':
case 'D':
snake.x[0] += 2;
break;
}
gotoxy(snake.x[0], snake.y[0]);
printf("●");
//如果蛇头吃到食物,蛇长加一
if (snake.x[0] == food.food_x && snake.y[0] == food.food_y)
{
flag = 1;
snake.length++;
score += 10;
//控制蛇的移动速度
if (speed > 70)
speed -= 50;
}
gotoxy(95, 0);//移除光标
}
//记录分数
void snake_SCORE()
{
gotoxy(85, 0);
color(14);
printf("当前分数为:%d", score);
}
//判断蛇死亡情况
int snake_DEATH()
{
for (int i = 0; i < MAP_WIDETH; i++)
{
if (snake.x[0] == 0 || snake.x[0] == MAP_WIDETH || snake.y[0] == 0 || snake.y[0] == MAP_HEAD - 1)
return 1;
}
for (int i = 1; i < snake.length; i++)
{
if (snake.x[0] == snake.x[i] && snake.y[0] == snake.y[i])
return 1;
}
return 0;
}
//开始游戏
void beginGame()
{
color(15);
gotoxy(MAP_WIDETH / 2, MAP_HEAD / 2);
printf("点击任意键开始游戏");
gotoxy(MAP_WIDETH / 2, MAP_HEAD / 2 + 1);
system("pause");//暂停
system("cls");//清屏
}
一、地图的绘制
绘制地图需要用到控制光标移动的函数:
void gotoxy(int x, int y)
{
COORD pos = { x,y };
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);// 获取标准输出设备句柄
SetConsoleCursorPosition(hOut, pos);//两个参数分别是指定哪个窗体,具体位置
}
其调用格式为goto(x,y),其中,x和y分别代表二维坐标中的x和y坐标,其使用方式为,使光标移动到控制面板的该位置,因此,我们可以使用两个循环来分别打印地图的上下边界和左右边界。注意:图形■横向占两格,竖向占一格,因此打印方式为:
for (int i = 1; i < MAP_WIDETH; i += 2) //地图宽80,高30
{
gotoxy(i, 0);//上边界
printf("■");
gotoxy(i, MAP_HEAD - 1);//下边界
printf("■");
}
for (int i = 1; i < MAP_HEAD; i++)
{
gotoxy(0, i);//左边界
printf("■");
gotoxy(MAP_WIDETH, i);//右边界
printf("■");
}
二、打印蛇
在打印蛇之前,需要先定义一个蛇的结构体,包括蛇的坐标,蛇的长度
struct Snake//蛇的坐标
{
int x[SNAKE_MAXLENGTH];
int y[SNAKE_MAXLENGTH];
int length;//蛇的长度
}snake;
注意在前方定义字符型常量 SNAKE_MAXLENGTH(蛇的最大长度)为100。
在打印蛇头及蛇身之前需要对蛇头的坐标以及蛇的初始长度进行初始化,蛇身用循环的方式打印。
snake.x[0] = MAP_WIDETH / 2;
snake.y[0] = MAP_HEAD / 2;
snake.length = initSIZE;
color(7);
//打印蛇头
gotoxy(snake.x[0], snake.y[0]);
printf("◎");
//循环打印蛇身
color(14);
for (int i = 1; i < snake.length; i++)
{
snake.x[i] = snake.x[i - 1] + 2;
snake.y[i] = snake.y[i - 1];
gotoxy(snake.x[i], snake.y[i]);
printf("○");
}
三、打印食物
随机在地图上生成食物,需要用到随机数,srand()和rand()函数(需要在前面包含头文件<stdlib.h>),注意:食物产生的位置不能为蛇的身体或者是墙壁与墙外
void snake_FOOD()
{
color(6);
srand(time(NULL));
//确保食物产生的位置的x坐标为偶数
here:while (1)
{
food.food_x = rand() % 76 + 2;
if (food.food_x % 2 != 0)
continue;
else
break;
}
food.food_y = rand() % 26 + 2;
//使食物不打印在蛇身上
for (int i = 1; i < snake.length; i++)
{
if (food.food_x == snake.x[i] && food.food_y == snake.y[i])
goto here;
}
gotoxy(food.food_x, food.food_y);
printf("■");
flag = 0;
gotoxy(70, 0);//移除光标
}
其中flag变量的作用是记录事物的产生情况,如果未产生食物则标记为1,产生之后标记为0
四、移动蛇
移动蛇的过程中需要通过按键来改变蛇移动的方向,在移动蛇的过程中注意:当蛇头每前进一格,就需要擦除其最后一节蛇尾,以此来保证蛇的长度不变,同时还应注意:当蛇向一个方向移动时,要避免其向反方向移动
void snake_MOVE()
{
int p;//记录输入的方向
color(10);
if (_kbhit())//监听是否有键盘输入
{
fflush(stdin);
p = _getch();
//防止蛇反方向移动
if (dir == 'w' || dir == 'W')
{
if (p != 's' && p != 'S')
dir = p;
}
if (dir == 's' || dir == 'S')
{
if (p != 'w' && p != 'W')
dir = p;
}
if (dir == 'a' || dir == 'A')
{
if (p != 'd' && p != 'D')
dir = p;
}
if (dir == 'd' || dir == 'D')
{
if (p != 'a' && p != 'A')
dir = p;
}
}
//擦除最后一节蛇身
gotoxy(snake.x[snake.length - 1], snake.y[snake.length - 1]);
printf(" ");
gotoxy(95, 0);//移除光标
//移动蛇
for (int i = snake.length - 1; i > 0; i--)
{
snake.x[i] = snake.x[i - 1];
snake.y[i] = snake.y[i - 1];
gotoxy(snake.x[i], snake.y[i]);
printf("○");
}
//通过按键改变蛇的方向
switch (dir)
{
case 'w':
case 'W':
snake.y[0]--;
break;
case 's':
case 'S':
snake.y[0]++;
break;
case 'a':
case 'A':
snake.x[0] -= 2;
break;
case 'd':
case 'D':
snake.x[0] += 2;
break;
}
gotoxy(snake.x[0], snake.y[0]);
printf("●");
//如果蛇头吃到食物,蛇长加一
if (snake.x[0] == food.food_x && snake.y[0] == food.food_y)
{
flag = 1;
snake.length++;
score += 10;
//控制蛇的移动速度
if (speed > 70)
speed -= 10;
}
gotoxy(95, 0);//移除光标
}
五、判断蛇死亡(吃到自己或者碰到墙)
游戏结束的标志分别为蛇投的坐标等于蛇身的任意一节的坐标,或者蛇头的坐标等于任意一块墙的坐标,当游戏运行情况满足这两种情况时游戏结束。
如果碰到墙或者碰到自己的身体的话向主函数返回1,并结束游戏
int snake_DEATH()
{
for (int i = 0; i < MAP_WIDETH; i++)
{
if (snake.x[0] == 0 || snake.x[0] == MAP_WIDETH || snake.y[0] == 0 || snake.y[0] == MAP_HEAD - 1)
return 1;
}
for (int i = 1; i < snake.length; i++)
{
if (snake.x[0] == snake.x[i] && snake.y[0] == snake.y[i])
return 1;
}
return 0;
}