1、功能说明
贪吃蛇游戏,随机产生蛇头和食物,可以调节游戏速度,自吃或碰壁死亡。
2、流程设计
本流程采用XMind软件,对于初学不适应正规流程图和伪语言等方法进行流程设计的人很适用,实际上个人觉得在结构上比正规流程图更加优化。
3、函数设计说明(11个)
void gotoxy(HANDLE hOut, int x, int y);
光标移动函数
void Button();
取出按键,并判断方向
void StartGame();
开始游戏显示界面
void ChooseLevel();
等级选择界面
void Initial();
地图的初始化,边框说明显示
void Create_Snake();
随机创建蛇头
void Create_Food();
随机产生食物
void Show();
刷新显示地图
void Move();
蛇的移动
void Check_Border();
检查蛇头是否越界
void Check_Head();
检查蛇头是否自撞
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
#include <time.h>
#include <windows.h>
#define H 20 //地图的高
#define L 20 //地图的长
stdio:基础语句 stdlib:srand随机数,system系统调用
conio:getch获取,kbhit判断按键 time:随机数种子
windows:光标操纵
int key; //按键保存
int sum = 1, over = 1; //蛇的长度, 游戏结束over=0(自吃或碰墙)
int dx[4] = {-2, 2, 0, 0}; //左、右、上、下的方向
int dy[4] = {0, 0, -1, 1};
int Level; //游戏等级
int speed; //对应刷新速度
HANDLE hOut; //定义句柄
key
储存按键
sum
储存蛇的长度
over
储存游戏状态
dx,dy对蛇的坐标进行更改(实现坐标移动的重点)
(如取dx[0],dy[0] 横坐标-2,纵坐标不变,即蛇左移一格)
Level储存游戏等级speed储存刷新速度 hOut为光标函数句柄
struct Snake //蛇的每个节点
{
int x; //横纵坐标
int y;
int now; //保存当前节点的方向, 0,1,2,3分别为左右上下
}Snake[H*L]={0,0,}; //初始化x,y为0
struct Food
{
int x,y;
}food;
定义蛇结构体储存位置和蛇头方向,定义食物结构体储存食物位置
void gotoxy(HANDLE hOut, int x, int y); //光标移动函数
void Button(); //取出按键,并判断方向
void StartGame(); //开始游戏显示界面
void ChooseLevel(); //等级选择界面
void Initial(); //地图的初始化,边框说明显示
void Create_Snake(); //随机创建蛇头
void Create_Food(); //随机产生食物
void Show(); //刷新显示地图
void Move(); //蛇的移动
void Check_Border(); //检查蛇头是否越界
void Check_Head(); //检查蛇头是否自撞
函数声明部分,具体功能请看注释。
main()
{
system("title 贪吃蛇~"); //控制台标题
int p=1; //循环控制变量
while(p)
{
over=1;
system("cls");
StartGame();
ChooseLevel();
system("cls");
Initial();
Create_Food();
Create_Snake();
Show();
gotoxy(hOut,8,11);
printf("是否要继续游戏~?(是,请输入1)\n\n ");
scanf("%d",&p);
}
}
将控制台标题设置为“贪吃蛇”,同时使用while实现游戏的循环。
while内步骤:初始化over为1。清屏执行StartGame,ChooseLevel,选择等级后清屏。Initial初始化游戏界面,Create_Food创造食物,Create_Snake创造蛇头,Show进行游戏。结束后显示结束界面。(可选择继续游戏,输入非0即可重新执行该步骤)
void gotoxy(HANDLE hOut, int x, int y)
{
COORD pos;
pos.X = x; //横坐标
pos.Y = y; //纵坐标
SetConsoleCursorPosition(hOut, pos);
}
光标函数,详情请百度,我也不算太清楚。该函数的作用是在控制台上将光标的位置移到x,y。
void Button() //取出按键,并判断方向
{
if(kbhit() != 0) //检查当前是否有键盘输入,若有则返回一个非0值,否则返回0
{
while(kbhit() != 0) //可能存在多个按键,要全部取完,以最后一个为主
key = getch(); //将按键从控制台中取出并保存到key中
switch(key)
{
case 75: //左
Snake[0].now = 0;
break;
case 77: //右
Snake[0].now = 1;
break;
case 72: //上
Snake[0].now = 2;
break;
case 80: //下
Snake[0].now = 3;
break;
}
}
}
按键判断函数,此外也可以使用宏定义的方法,但这种方法相对好理解也容易写,所以采用这种方法来获取上下左右按键。
void StartGame()
{
hOut=GetStdHandle(STD_OUTPUT_HANDLE);
gotoxy(hOut,15,5);
printf("cjq同学的贪吃蛇蛇~");
}
cjq是我的名字~~~,那个句柄就是开局定义一次,具体不懂,请百度。gotoxy函数把光标移动到横坐标15,纵坐标5的位置,然后打印“cjq同学的贪吃蛇蛇~”
void ChooseLevel()
{
gotoxy(hOut,10,10);
printf("请输入游戏等级(1-5级,5为最难)\n\n ");
scanf("%d",&Level);
if(Level<=5&&Level>=1)
{
speed=600-100*Level;
}
else
{
printf("\n 输入错误!\n");
ChooseLevel();
}
}
等级选择函数,开局可选择1-5,5个等级,每个等级的刷新速度分别为500,400,300,200,100(毫秒),也可理解为蛇的自然移动速度。
void Initial()
{
int x,y;
for(x=2;x<46;x+=2)
{
gotoxy(hOut,x,1);
printf("■");
}
for(x=2;x<46;x+=2)
{
gotoxy(hOut,x,22);
printf("■");
}
for(y=2;y<22;y++)
{
gotoxy(hOut,2,y);
printf("■");
}
for(y=2;y<22;y++)
{
gotoxy(hOut,44,y);
printf("■");
}
gotoxy(hOut,48,1);
printf("≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡");
gotoxy(hOut,48,2);
printf(" 欢迎来到贪吃蛇的世界");
gotoxy(hOut,48,3);
printf("≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡");
gotoxy(hOut,48,4);
printf(" 当前等级");
gotoxy(hOut,48,5);
printf(" LV%d",Level);
gotoxy(hOut,48,6);
printf("≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡");
gotoxy(hOut,59,7);
printf("键位说明");
gotoxy(hOut,48,8);
printf("≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡");
gotoxy(hOut,48,10);
printf(" 上 ↑ 下 ↓ 左 ← 右→");
gotoxy(hOut,48,12);
printf("● 蛇头 ○ 蛇身");
gotoxy(hOut,48,14);
printf("■ 边框 ★ 食物");
gotoxy(hOut,48,16);
printf("≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡");
gotoxy(hOut,48,18);
printf("当前分数:0");
gotoxy(hOut,48,20);
printf("已吃食物个数:0");
gotoxy(hOut,48,22);
printf("蛇的长度:0");
}
界面初始化,打印提示栏和边框。
void Create_Snake()
{
int hx,hy,x,y;
srand(time(0)); //随机种子
hx = rand()%20; //产生蛇头
hy = rand()%20;
x=2*hx+4;
y=hy+2;
gotoxy(hOut,x,y);
printf("●");
Snake[0].x = x;
Snake[0].y = y;
Snake[0].now=-1;
}
生成蛇头函数,使用time随机种子,具体怎么实现随机也请百度~
void Create_Food()
{
int x,y,i,m=0;
srand(time(0)); //随机种子
x = rand()%5;
y = rand()%5;
food.x=8*x+4;
food.y=4*y+3;
gotoxy(hOut,food.x,food.y);
for(i=0;i<sum;i++)
{
if(food.x!=Snake[0].x||food.y!=Snake[0].y)
m++;
}
if(m==sum)
printf("★");
else
Create_Food();
}
递归调用,确保食物位置不在蛇身上,若随机在蛇身上则重新执行函数。
void Show()
{
int i;
getch(); //先接受一个按键,使蛇开始往该方向走
Button();
while(over)
{
_sleep(speed); //延迟半秒(1000为1s)
Button(); //先判断按键在移动
Move();
Check_Border();
Check_Head();
gotoxy(hOut,48,18);
printf("当前分数:%d",Level*(sum-1));
gotoxy(hOut,48,20);
printf("已吃食物个数:%d",(sum-1));
gotoxy(hOut,48,22);
printf("蛇的长度:%d",sum);
if(over==0)
{
for(i=0;i<sum;i++)
{
Snake[i].x=0;
Snake[i].y=0;
}
system("cls");
gotoxy(hOut,14,5);
printf("游戏结束!");
gotoxy(hOut,13,7);
printf(">_< >_< >_<");
gotoxy(hOut,13,9);
printf(" 分数:%d\n",Level*(sum-1));
sum=1;
}
}
}
当游戏状态over为1的时候循环运行。根据等级不同设置不同的时间延迟,同时刷新输出分数等显示项。若游戏结束over为0则显示结束界面,并重置sum。
void Move() //蛇的移动
{
int i;
int x=Snake[0].x;
int y=Snake[0].y;
for(i=0;i<sum;i++)
{
gotoxy(hOut,Snake[i].x,Snake[i].y);
printf(" ");
}
if(food.x==Snake[0].x&&food.y==Snake[0].y)
{
sum=sum+1;
Snake[sum-1].x=x;
Snake[sum-1].y=y;
Create_Food();
}
else
{
if(sum!=1)
{
for(i=1;i<sum-1;i++)
{
Snake[i].x = Snake[i+1].x ;
Snake[i].y = Snake[i+1].y ;
}
Snake[sum-1].x = x;
Snake[sum-1].y = y;
}
}
Snake[0].x = Snake[0].x + dx[ Snake[0].now ];
Snake[0].y = Snake[0].y + dy[ Snake[0].now ];
gotoxy(hOut,Snake[0].x,Snake[0].y);
printf("●");
for(i=1;i<sum;i++)
{
gotoxy(hOut,Snake[i].x,Snake[i].y);
printf("○");
}
}
代码灵魂部分,实现蛇的移动。首先清空上次蛇的位置,并且判断是否吃到食物。若吃到食物则sum+1,将原蛇头位置定为新加节点并生成新食物。若没有吃到则节点前移一位最后一位的节点移动到原蛇头位置。蛇头通过
dx dy的运算确定新位置。打印出新的蛇头和蛇身。此处可以使用链表,因为我们没有学链表内容,所以没有采用。(关于栈的知识可以参考计算机导论或数据结构方面的书籍)。
void Check_Border()
{
if(Snake[0].x < 3|| Snake[0].x > 43||Snake[0].y < 2 || Snake[0].y >22)
over = 0;
}
如果蛇头坐标与边框坐标重合则判定为越界,置over为0,结束游戏。
void Check_Head()
{
int i,j;
for(i=0;i<sum;i++)
{
for(j=0;j<sum;j++)
{
if((Snake[j].x==Snake[i].x)&&(Snake[j].y==Snake[i].y)&&(j!=i))
over=0;
}
}
}
如果蛇头位置与蛇身重合则判定为自撞,置over为0,结束游戏。(有缺陷,当蛇身长度为2的时候反向移动无法判定为自撞)
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
#include <time.h>
#include <windows.h>
#define H 20 //地图的高
#define L 20 //地图的长
//全局变量定义
int key; //按键保存
int sum = 1, over = 1; //蛇的长度, 游戏结束over=0(自吃或碰墙)
int dx[4] = {-2, 2, 0, 0}; //左、右、上、下的方向
int dy[4] = {0, 0, -1, 1};
int Level; //游戏等级
int speed; //对应刷新速度
HANDLE hOut; //定义句柄
//结构体定义
struct Snake //蛇的每个节点
{
int x; //横纵坐标
int y;
int now; //保存当前节点的方向, 0,1,2,3分别为左右上下
}Snake[H*L]={0,0,}; //初始化x,y为0
struct Food
{
int x,y;
}food;
//函数定义
void gotoxy(HANDLE hOut, int x, int y); //光标移动函数
void Button(); //取出按键,并判断方向
void StartGame(); //开始游戏显示界面
void ChooseLevel(); //等级选择界面
void Initial(); //地图的初始化,边框说明显示
void Create_Snake(); //随机创建蛇头
void Create_Food(); //随机产生食物
void Show(); //刷新显示地图
void Move(); //蛇的移动
void Check_Border(); //检查蛇头是否越界
void Check_Head(); //检查蛇头是否自撞
//主函数
main()
{
system("title 贪吃蛇~"); //控制台标题
int p=1; //循环控制变量
while(p)
{
over=1;
system("cls");
StartGame();
ChooseLevel();
system("cls");
Initial();
Create_Food();
Create_Snake();
Show();
gotoxy(hOut,8,11);
printf("是否要继续游戏~?(是,请输入1)\n\n ");
scanf("%d",&p);
}
}
//分函数
void gotoxy(HANDLE hOut, int x, int y)
{
COORD pos;
pos.X = x; //横坐标
pos.Y = y; //纵坐标
SetConsoleCursorPosition(hOut, pos);
}
void Button() //取出按键,并判断方向
{
if(kbhit() != 0) //检查当前是否有键盘输入,若有则返回一个非0值,否则返回0
{
while(kbhit() != 0) //可能存在多个按键,要全部取完,以最后一个为主
key = getch(); //将按键从控制台中取出并保存到key中
switch(key)
{
case 75: //左
Snake[0].now = 0;
break;
case 77: //右
Snake[0].now = 1;
break;
case 72: //上
Snake[0].now = 2;
break;
case 80: //下
Snake[0].now = 3;
break;
}
}
}
void StartGame()
{
hOut=GetStdHandle(STD_OUTPUT_HANDLE);
gotoxy(hOut,15,5);
printf("cjq同学的贪吃蛇蛇~");
}
void ChooseLevel()
{
gotoxy(hOut,10,10);
printf("请输入游戏等级(1-5级,5为最难)\n\n ");
scanf("%d",&Level);
if(Level<=5&&Level>=1)
{
speed=600-100*Level;
}
else
{
printf("\n 输入错误!\n");
ChooseLevel();
}
}
void Initial()
{
int x,y;
for(x=2;x<46;x+=2)
{
gotoxy(hOut,x,1);
printf("■");
}
for(x=2;x<46;x+=2)
{
gotoxy(hOut,x,22);
printf("■");
}
for(y=2;y<22;y++)
{
gotoxy(hOut,2,y);
printf("■");
}
for(y=2;y<22;y++)
{
gotoxy(hOut,44,y);
printf("■");
}
gotoxy(hOut,48,1);
printf("≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡");
gotoxy(hOut,48,2);
printf(" 欢迎来到贪吃蛇的世界");
gotoxy(hOut,48,3);
printf("≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡");
gotoxy(hOut,48,4);
printf(" 当前等级");
gotoxy(hOut,48,5);
printf(" LV%d",Level);
gotoxy(hOut,48,6);
printf("≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡");
gotoxy(hOut,59,7);
printf("键位说明");
gotoxy(hOut,48,8);
printf("≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡");
gotoxy(hOut,48,10);
printf(" 上 ↑ 下 ↓ 左 ← 右→");
gotoxy(hOut,48,12);
printf("● 蛇头 ○ 蛇身");
gotoxy(hOut,48,14);
printf("■ 边框 ★ 食物");
gotoxy(hOut,48,16);
printf("≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡");
gotoxy(hOut,48,18);
printf("当前分数:0");
gotoxy(hOut,48,20);
printf("已吃食物个数:0");
gotoxy(hOut,48,22);
printf("蛇的长度:0");
}
void Create_Snake()
{
int hx,hy,x,y;
srand(time(0)); //随机种子
hx = rand()%20; //产生蛇头
hy = rand()%20;
x=2*hx+4;
y=hy+2;
gotoxy(hOut,x,y);
printf("●");
Snake[0].x = x;
Snake[0].y = y;
Snake[0].now=-1;
}
void Create_Food()
{
int x,y,i,m=0;
srand(time(0)); //随机种子
x = rand()%5;
y = rand()%5;
food.x=8*x+4;
food.y=4*y+3;
gotoxy(hOut,food.x,food.y);
for(i=0;i<sum;i++)
{
if(food.x!=Snake[0].x||food.y!=Snake[0].y)
m++;
}
if(m==sum)
printf("★");
else
Create_Food();
}
void Show()
{
int i;
getch(); //先接受一个按键,使蛇开始往该方向走
Button();
while(over)
{
_sleep(speed); //延迟半秒(1000为1s)
Button(); //先判断按键在移动
Move();
Check_Border();
Check_Head();
gotoxy(hOut,48,18);
printf("当前分数:%d",Level*(sum-1));
gotoxy(hOut,48,20);
printf("已吃食物个数:%d",(sum-1));
gotoxy(hOut,48,22);
printf("蛇的长度:%d",sum);
if(over==0)
{
for(i=0;i<sum;i++)
{
Snake[i].x=0;
Snake[i].y=0;
}
system("cls");
gotoxy(hOut,14,5);
printf("游戏结束!");
gotoxy(hOut,13,7);
printf(">_< >_< >_<");
gotoxy(hOut,13,9);
printf(" 分数:%d\n",Level*(sum-1));
sum=1;
}
}
}
void Move() //蛇的移动
{
int i;
int x=Snake[0].x;
int y=Snake[0].y;
for(i=0;i<sum;i++)
{
gotoxy(hOut,Snake[i].x,Snake[i].y);
printf(" ");
}
if(food.x==Snake[0].x&&food.y==Snake[0].y)
{
sum=sum+1;
Snake[sum-1].x=x;
Snake[sum-1].y=y;
Create_Food();
}
else
{
if(sum!=1)
{
for(i=1;i<sum-1;i++)
{
Snake[i].x = Snake[i+1].x ;
Snake[i].y = Snake[i+1].y ;
}
Snake[sum-1].x = x;
Snake[sum-1].y = y;
}
}
Snake[0].x = Snake[0].x + dx[ Snake[0].now ];
Snake[0].y = Snake[0].y + dy[ Snake[0].now ];
gotoxy(hOut,Snake[0].x,Snake[0].y);
printf("●");
for(i=1;i<sum;i++)
{
gotoxy(hOut,Snake[i].x,Snake[i].y);
printf("○");
}
}
void Check_Border()
{
if(Snake[0].x < 3|| Snake[0].x > 43||Snake[0].y < 2 || Snake[0].y >22)
over = 0;
}
void Check_Head()
{
int i,j;
for(i=0;i<sum;i++)
{
for(j=0;j<sum;j++)
{
if((Snake[j].x==Snake[i].x)&&(Snake[j].y==Snake[i].y)&&(j!=i))
over=0;
}
}
}
奉上全部代码以供参考借鉴
刚刚学习编程的同学一定要注重设计程序,代码好写,问题好解决,而一个程序的灵魂在于设计阶段。随机数,光标,键位控制学校都不会教给你,需要你有足够的自学能力,并在短时间能够学以致用。即便是几十年前的C语言,水也没有我们想象的那么浅。c语言作为面向过程的编程,其函数与面向对象编程中的类有所关联,因而可以说C在一定程度上对C++乃至java的学习都有所帮助(至于jc,Python,php等,小生不敢妄言,大体自学过c++和java,因而感触比较深)。同样也要养成良好的编写习惯,在写代码的时候尽量写一些注释,不必长篇大论,但至少一个月之后再看这段代码依然知道它写的大概是什么而不是重读重调。其次,或许是因为强迫症,我在写代码的时候习惯性括号后空两格,同括号等级对齐,括号单占一行,函数之间留空行,优先声明全部函数,主函数写在分函数前,main前不加void或int…… 当然,这些都是我个人习惯,大家在看我的代码的时候应该能体会到,如果没符合以上要求,那就是复制粘贴过来的时候排版变了~
总之,想写贪吃蛇的同学借鉴这篇文章想来写出贪吃蛇小游戏也不太难了,如果想包装一下的话可以试windows编程中的窗口对话框什么的,因为cfree不太支持我也就没尝试。因为编译环境,这个代码在vs或者dev里可能出现一些问题,大家自己调试一下就行(比如vs的安全度问题)欢迎评论区批评指责~