我就学了个C语言,就不能写贪吃蛇?
一、主函数的搭建
int main()
{
setdata();//数据初始化
while(1)
{
show(); //画面打印
updatewithoutinput(); //与输入无关的更新
updatewithinput(); //与输入有关的更新
}
return 0;
}
说明:
setdata是对程序的初始化
while(1)表示死循环,只有游戏结束才跳出循环
updatewithoutinput是和输入无关的更新(蛇吃到食物,蛇向前走,撞墙等等)
updatewithdata是和输入有关的更新(蛇的转向)
二、全局变量的设定
#define high 20 //定义地图的高度
#define width 30 //定义地图的宽度
#define snake_maxlength 200 //蛇长最大值
int snake_x[snake_maxlength],snake_y[snake_maxlength]; //蛇的坐标
int snake_length; //蛇现在的长度
int map[high][width]={0}; //初始化地图,0表示空格,1表示蛇身,2表示食物,
//3表示墙,4表示蛇头,现在地图中什么也没有
int food_x,food_y; //食物的坐标
int score; //本局分数
char input; //读取用户键入的字符
说明:
地图的宽度和高度,蛇的最大长度也可以由用户定义,程序员自己决定
三、对数据的初始化
void setdata()
{
snake_x[0]=high/2;snake_y[0]=width/3; //初始化蛇头,在地图内的位置随意
snake_x[1]=high/2;snake_y[1]=width/3+1; //初始化蛇身
snake_length=2; //蛇的原始尺寸(一格头一格身子)
food_x=high/3,food_y=width/3; //初始化食物的位置,在地图内位置随意
score=0;
int i,j;
map[snake_x[0]][snake_y[0]]=4; //蛇头
map[snake_x[1]][snake_y[1]]=1; //蛇身
map[food_x][food_y]=2; //食物
for(i=0;i<high;i++) //墙
for(j=0;j<width;j++)
if(i==0||j==0||i==high-1||j==width-1)
map[i][j]=3;
}
说明:
地图中0表示空格,1表示蛇身,2表示食物,3表示墙,4表示蛇头
对墙的初始化是当行数为零或最后一行,列数为0或最后一行时,地图显示墙
四、画面打印
void gotoxy(int x,int y) //解释1
{
HANDLE handle=GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos;
pos.X=x;
pos.Y=y;
SetConsoleCursorPosition(handle,pos);
}
void show()
{
gotoxy(0,0);
int i,j;
for(i=0;i<high;i++)
{
for(j=0;j<width;j++)
{
if(map[i][j]==0)
cout<<" ";
else if(map[i][j]==1)
cout<<"+";
else if(map[i][j]==2)
cout<<"@";
else if(map[i][j]==3)
cout<<"*";
else if(map[i][j]==4)
cout<<"0";
}
printf("\n");
}
printf("得分:%d",score);
}
解释1:
gotoxy相当于清屏并返回某个点重新开始打印,但不会存在闪屏问题,具体为啥我也不太懂,o( ̄︶ ̄)o
gotoxy需要加载#include<windows.h>头文件
打印出来是这个样子
五、与输入无关的函数
void updatewithoutinput()
{
int i;
if(snake_x[0]==food_x&&snake_y[0]==food_y)
{
score++;
srand((unsigned)time(NULL)); //srand和rand函数连用产生随机数,一般以时间作为产生随机数的种子
food_x=rand()%(high-3)+1; //解释2
food_y=rand()%(width-3)+1;
map[food_x][food_y]=2;
snake_length++;
}
if(snake_x[0]==0||snake_x[0]==high-1||snake_y[0]==0||snake_y[0]==width-1) //碰墙了
{
cout<<"游戏结束!"<<endl;
getch();
exit(1); //解释3
}
for(i=1;i<snake_length;i++) //碰到自己了 ,遍历蛇身看是否和蛇头重复
if(snake_x[0]==snake_x[i]&&snake_y[0]==snake_y[i])
{
cout<<"游戏结束"<<endl;
getch();
exit(1);
}
}
解释2:
srand和rand函数怎么用自己百度,这里解释一下为什么是rand()%(high-3)+1.我们需要确保函数产生的随机数在地图范围内,rand()%(high-3)的意思是产生0~(high-3)的随机数,如果high是20就是产生0-17的随机数,再加一随机数就是1-18,恰好在地图范围内(地图范围是0行到19行,其中第0行和第19行是墙)
解释3:
exit(1)表示执行到此时程序结束
六、与输入有关的更新
void updatewithinput()
{
int i=1;
if(kbhit()) //解释4
input=getch();
if(input=='w'||input=='a'||input=='s'||input=='d')
{
map[snake_x[snake_length-1]][snake_y[snake_length-1]]=0;//蛇走过一格,蛇尾处变为空格
for(i=snake_length-1;i>0;i--)
{
snake_x[i]=snake_x[i-1]; //走过一格后蛇身的坐标覆盖前面的坐标 (从后往前覆盖,蛇头也会被蛇身覆盖)
snake_y[i]=snake_y[i-1]; //解释5
map[snake_x[i]][snake_y[i]]=1;
}
}
if(input=='w')
snake_x[0]--;
else if(input=='s')
snake_x[0]++;
else if(input=='a')
snake_y[0]--;
else if(input=='d')
snake_y[0]++;
map[snake_x[0]][snake_y[0]]=4; //蛇头的坐标覆盖前面的坐标
}
解释4:
kbhit()是判断是否有输入,有的话返回1
getch()是读入一个字符并返回,类似于scanf,但不需要回车键确认
解释5:
有两种情况,蛇没有吃食物和吃了食物,此段代码两种情况都能解决(让蛇从第二个节点开始等于前一个节点,让最后一个节点变为0)
完整代码如下
#include<iostream>
#include<windows.h>
#include<time.h>
#include<stdlib.h>
#include<conio.h>
using namespace std;
#define high 20 //定义地图的高度
#define width 30 //定义地图的宽度
#define snake_maxlength 200 //蛇长最大值
int snake_x[snake_maxlength],snake_y[snake_maxlength]; //蛇的坐标
int snake_length; //蛇现在的长度
int map[high][width]={0}; //初始化地图,0表示空格,1表示蛇身,2表示食物,
//3表示墙,4表示蛇头,现在地图中什么也没有
int food_x,food_y; //食物的坐标
int score; //本局分数
char input; //读取用户键入的字符
//数据的初始化函数
void setdata()
{
snake_x[0]=high/2;snake_y[0]=width/3; //初始化蛇头,在地图内的位置随意
snake_x[1]=high/2;snake_y[1]=width/3+1; //初始化蛇身
snake_length=2; //蛇的原始尺寸(一格头一格身子)
food_x=high/3,food_y=width/3; //初始化食物的位置,在地图内位置随意
score=0;
int i,j;
map[snake_x[0]][snake_y[0]]=4; //蛇头
map[snake_x[1]][snake_y[1]]=1; //蛇身
map[food_x][food_y]=2; //食物
for(i=0;i<high;i++) //墙
for(j=0;j<width;j++)
if(i==0||j==0||i==high-1||j==width-1)
map[i][j]=3;
}
//画面打印
void gotoxy(int x,int y) //解释1
{
HANDLE handle=GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos;
pos.X=x;
pos.Y=y;
SetConsoleCursorPosition(handle,pos);
}
void show()
{
gotoxy(0,0);
int i,j;
for(i=0;i<high;i++)
{
for(j=0;j<width;j++)
{
if(map[i][j]==0)
cout<<" ";
else if(map[i][j]==1)
cout<<"+";
else if(map[i][j]==2)
cout<<"@";
else if(map[i][j]==3)
cout<<"*";
else if(map[i][j]==4)
cout<<"0";
}
printf("\n");
}
printf("得分:%d",score);
}
//与输入无关的更新
void updatewithoutinput()
{
int i;
if(snake_x[0]==food_x&&snake_y[0]==food_y)
{
score++;
srand((unsigned)time(NULL)); //srand和rand函数连用产生随机数,一般以时间作为产生随机数的种子
food_x=rand()%(high-3)+1; //解释2
food_y=rand()%(width-3)+1;
map[food_x][food_y]=2;
snake_length++;
}
if(snake_x[0]==0||snake_x[0]==high-1||snake_y[0]==0||snake_y[0]==width-1) //碰墙了
{
cout<<"游戏结束!"<<endl;
getch();
exit(1); //解释3
}
for(i=1;i<snake_length;i++) //碰到自己了 ,遍历蛇身找是否和蛇头重复
if(snake_x[0]==snake_x[i]&&snake_y[0]==snake_y[i])
{
cout<<"游戏结束"<<endl;
getch();
exit(1);
}
}
void updatewithinput()
{
int i=1;
if(kbhit()) //解释4
input=getch();
if(input=='w'||input=='a'||input=='s'||input=='d')
{
map[snake_x[snake_length-1]][snake_y[snake_length-1]]=0;//蛇走过一格,蛇尾处变为空格
for(i=snake_length-1;i>0;i--)
{
snake_x[i]=snake_x[i-1]; //走过一格后蛇身的坐标覆盖前面的坐标 (从后往前覆盖,蛇头也会被蛇身覆盖)
snake_y[i]=snake_y[i-1]; //解释5
map[snake_x[i]][snake_y[i]]=1;
}
}
if(input=='w')
snake_x[0]--;
else if(input=='s')
snake_x[0]++;
else if(input=='a')
snake_y[0]--;
else if(input=='d')
snake_y[0]++;
map[snake_x[0]][snake_y[0]]=4; //蛇头的坐标覆盖前面的坐标
}
int main()
{
setdata();//数据初始化
while(1)
{
show(); //画面打印
updatewithoutinput(); //与输入无关的更新
updatewithinput(); //与输入有关的更新
}
return 0;
}
但这只是游戏最简单的实现,还可以添加一些稍微完善一点的功能,感兴趣的可以继续往下看
蛇的移动速度
上面的代码中未提及这一点,蛇的速度可以靠Sleep()延时函数来实现,Sleep函数的作用是让程序暂停若干时间,以毫秒为单位,比如Sleep(1000)就是程序暂停1s。我们对updatewithoutinput函数(或者updatewithinput或者主函数均可)稍作改动即可
void updatewithoutinput()
{
Sleep(200); //睡眠200毫秒,即蛇每200毫秒走一格
int i;
if(snake_x[0]==food_x&&snake_y[0]==food_y)
{
score++;
.......
那我要是想让用户自己输入移动速度呢?可对主函数做一些更改,代码如下
int main()
{
setdata();//数据初始化
int op;
cout<<"请输入移动一格的毫秒数";
cin>>op;
while(1)
{
show(); //画面打印
Sleep(op);
updatewithoutinput(); //与输入无关的更新
updatewithinput(); //与输入有关的更新
}
return 0;
}
那我玩游戏的时候要调节蛇的速度呢?稍后讨论
游戏暂停不能指定按键么?
上述代码运行过程中可以发现,用户键入的不是wasd的话,蛇会暂停,再次键入方向键,蛇会继续运行。这是因为当input不是wasd的话,蛇头和蛇身都不会发生变动。这可以算是一个bug,考不考虑程序员自己决定,现在我们决定对程序做一些改进
首先要在函数开始的时候定义一个全局变量primeinput(直译为input之前,他的作用是当input发生变化时保留input)。定义完primeinput后,函数的变动如下:
void updatewithinput()
{
int i=1;
if(kbhit())
input=getch();
//解释1
if(input!='\0') //input!='\0'(空)表示当有输入的时候开始变动图像,这是游戏开始的条件
{
if(input=='w'||input=='a'||input=='s'||input=='d') //解释2
primeinput=input;
else if(primeinput=='.') //解释3
primeinput='w';
map[snake_x[snake_length-1]][snake_y[snake_length-1]]=0;//蛇走过一格,蛇尾处变为空格
for(i=snake_length-1;i>0;i--)
{
snake_x[i]=snake_x[i-1]; //走过一格后蛇身的坐标覆盖前面的坐标 (从后往前覆盖,蛇头也会被蛇身覆盖)
snake_y[i]=snake_y[i-1];
map[snake_x[i]][snake_y[i]]=1;
}
}
//注意这里从判断input变成判断primeinput
if(primeinput=='w')
snake_x[0]--;
else if(primeinput=='s')
snake_x[0]++;
else if(primeinput=='a')
snake_y[0]--;
else if(primeinput=='d')
snake_y[0]++;
map[snake_x[0]][snake_y[0]]=4; //蛇头的坐标覆盖前面的坐标
}
定义全局变量primeinput的时候可以赋初值,我们赋的初值是‘.’,初值是任意的(只要不是wasd就行),或者干脆不赋初值,系统会自动分配初值为空(此时解释3处的判断条件要改为primeinput=’\0’)
解释1:(这个比较难懂,看不明白可以先往下看)
当没有输入的时候,input就是系统给赋的初值(也就是’\0’),这样if条件没有达成,primeinput仍然是’.’,所以判断primeinput不等于wasd,snake_x[0]和snake_y[0]也不会发生变动,及show函数所展现的画面没有变(狭义可以理解为函数还没有开始,其实程序在打开的时候就在一直刷新,只不过展现画面还没有开始变化)
解释2、解释3:
先分析一下第一次输入,当输入w/a/s/d时,primeinput对应改变,那要是不是w/a/s/d呢?这时候就要出现问题了,因为已经进入if内了,input不是w/a/s/d也要执行if语句里的内容,这时可以给primeinput赋个初值,这时就认为输入任意字符表示游戏开始,如果输入的不是w/a/s/d的话,相当于输入了w。
bug修复完了,到底怎么暂停
暂停有很多方法,这里举例一种,用户按p表示游戏暂停,在此按p表示游戏继续,代码如下:
void updatewithinput()
{
int i=1;
if(kbhit())
input=getch();
if(input!='\0') //input!='\0'(空)表示当有输入的时候开始执行函数,这是游戏开始的条件
{
if(input=='w'||input=='a'||input=='s'||input=='d')
primeinput=input;
else if(primeinput=='.')
primeinput='w';
else if(input=='p') //当输入p的时候跳入while(1)死循环
{
while(1)
{
char op;
op=getch();
if(op=='p')
{
input='.'; //解释1
break; //再次输入p是跳出
}
}
}
........
解释1:
挑出的时候不要忘了input还等于’p’呢,为了防止下轮循环系统还认为用户输入了暂停键,在跳出前为input赋值(随意赋值,不过我喜欢用‘.’)
蛇怎么能往后走呢?我以前玩的不是这样啊!
啊,刚刚的程序我们的贪吃蛇竟然可以回头,奇怪,以前玩的版本不是这样啊!
有两种解决方案,
方案一:我就是不让你回头 代码如下
void updatewithinput()
{
int i=1;
if(kbhit())
input=getch();
// if(input=='w'||input=='a'||input=='s'||input=='d')
if(input!='\0') //input!='\0'(空)表示当有输入的时候开始执行函数,这是游戏开始的条件
{
if(input=='w'||input=='a'||input=='s'||input=='d')
{
if(primeinput=='w'&&input=='s'||primeinput=='s'&&input=='w'||primeinput=='a'&&input=='d'||primeinput=='d'&&input=='a')
; //解释1
else
primeinput=input;
}
.......
解释1:
当primeinput也就是蛇的原始走向和input也就是用户当前的输入相反时,程序什么也不干,无视此次输入,否则,按常规流程处理
方案二:我的游戏我做主 比如下面这段代码
if(primeinput=='w'&&input=='s'||primeinput=='s'&&input=='w'||primeinput=='a'&&input=='d'||primeinput=='d'&&input=='a')
{
cout<<"贪吃蛇教会你的,是世上没有回头路,回头等于自焚"<<endl;
exit(0); //退出程序
}
小小的diy一下,你也可以说“啊,XXX(一个你憎恨的人,呵呵)的脖子断啦~”
稍微正经一点,我想在玩游戏的时候调整蛇的速度
首先把表示速度的变量op设置成全局变量,这样你既可以在游戏中操控也可以在开始的时候操控,代码如下:
if(input!='\0') //input!='\0'(空)表示当有输入的时候开始执行函数,这是游戏开始的条件
{
if(input=='w'||input=='a'||input=='s'||input=='d')
{
if(primeinput=='w'&&input=='s'||primeinput=='s'&&input=='w'||primeinput=='a'&&input=='d'||primeinput=='d'&&input=='a')
{
cout<<"贪吃蛇教会你的,是世上没有回头路,回头等于自焚"<<endl;
exit(0); //退出程序
}
else
primeinput=input;
}
else if(primeinput=='.')
primeinput='w';
else if(input=='p')
{
while(1)
{
char op;
op=getch();
if(op=='p')
{
input='.';
break;
}
}
}
else if(input=='+')
{
if(op>=50) //表示最快速度为25 ms刷新一次
op=op/2; //输入‘+’号,刷新的毫秒数减半
}
else if(input=='-')
{
if(op<=1000) //表示最慢速度为 2000 ms刷新一次
op=op*2;
}
程序里的部分函数可以从算法上优化,这里就不说了(毕竟不是本文重点,哈哈),程序也有很多可扩充的功能有待读者自己探讨(比如说蛇从这面墙穿进去从另一面墙穿出来,食物是不是得考虑刷在蛇的体内怎么办,地图的大小用户能不能定义,地图中能不能有障碍物,玩完这一局能不能自动开下一局)
有些挑战性的是,能不能两个人一起玩(简易版的贪吃蛇大作战),我最近也在做,后续还会更新(欢迎来探讨呀)
附上最终版的代码:
#include<iostream>
#include<windows.h>
#include<time.h>
#include<stdlib.h>
#include<conio.h>
using namespace std;
#define high 20 //定义地图的高度
#define width 30 //定义地图的宽度
#define snake_maxlength 200 //蛇长最大值
int snake_x[snake_maxlength],snake_y[snake_maxlength]; //蛇的坐标
int snake_length; //蛇现在的长度
int map[high][width]={0}; //初始化地图,0表示空格,1表示蛇身,2表示食物,
//3表示墙,4表示蛇头,现在地图中什么也没有
int food_x,food_y; //食物的坐标
int score; //本局分数
char input; //读取用户键入的字符
char primeinput='.'; //primeinput在input脱离wasd时,用来存储input
int op; //速度控制全局变量
//数据的初始化函数
void setdata()
{
snake_x[0]=high/2;snake_y[0]=width/3; //初始化蛇头,在地图内的位置随意
snake_x[1]=high/2;snake_y[1]=width/3+1; //初始化蛇身
snake_length=2; //蛇的原始尺寸(一格头一格身子)
food_x=high/3,food_y=width/3; //初始化食物的位置,在地图内位置随意
score=0;
int i,j;
map[snake_x[0]][snake_y[0]]=4; //蛇头
map[snake_x[1]][snake_y[1]]=1; //蛇身
map[food_x][food_y]=2; //食物
for(i=0;i<high;i++) //墙
for(j=0;j<width;j++)
if(i==0||j==0||i==high-1||j==width-1)
map[i][j]=3;
}
//画面打印
void gotoxy(int x,int y)
{
HANDLE handle=GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos;
pos.X=x;
pos.Y=y;
SetConsoleCursorPosition(handle,pos);
}
void show()
{
gotoxy(0,0);
int i,j;
for(i=0;i<high;i++)
{
for(j=0;j<width;j++)
{
if(map[i][j]==0)
cout<<" ";
else if(map[i][j]==1)
cout<<"+";
else if(map[i][j]==2)
cout<<"@";
else if(map[i][j]==3)
cout<<"*";
else if(map[i][j]==4)
cout<<"0";
}
printf("\n");
}
printf("得分:%d",score);
}
//与输入无关的更新
void updatewithoutinput()
{
int i;
if(snake_x[0]==food_x&&snake_y[0]==food_y)
{
score++;
srand((unsigned)time(NULL)); //srand和rand函数连用产生随机数,一般以时间作为产生随机数的种子
food_x=rand()%(high-3)+1;
food_y=rand()%(width-3)+1;
map[food_x][food_y]=2;
snake_length++;
}
if(snake_x[0]==0||snake_x[0]==high-1||snake_y[0]==0||snake_y[0]==width-1) //碰墙了
{
cout<<"游戏结束!"<<endl;
getch();
exit(1);
}
for(i=1;i<snake_length;i++) //碰到自己了 ,遍历蛇身找是否和蛇头重复
if(snake_x[0]==snake_x[i]&&snake_y[0]==snake_y[i])
{
cout<<"游戏结束"<<endl;
getch();
exit(1);
}
}
void updatewithinput()
{
int i=1;
if(kbhit())
input=getch();
if(input!='\0') //input!='\0'(空)表示当有输入的时候开始更新画面,这是游戏开始的条件
{
if(input=='w'||input=='a'||input=='s'||input=='d')
{
if(primeinput=='w'&&input=='s'||primeinput=='s'&&input=='w'||primeinput=='a'&&input=='d'||primeinput=='d'&&input=='a')
{
cout<<"贪吃蛇教会你的,是世上没有回头路,回头等于自焚"<<endl;
exit(0); //退出程序
}
else
primeinput=input;
}
else if(primeinput=='.')
primeinput='w';
else if(input=='p')
{
while(1)
{
char op;
op=getch();
if(op=='p')
{
input='.';
break;
}
}
}
else if(input=='+')
{
if(op>=50) //表示最快速度为25 ms刷新一次
op=op/2;
}
else if(input=='-')
{
if(op<=1000) //表示最慢速度为 2000 ms刷新一次
op=op*2;
}
map[snake_x[snake_length-1]][snake_y[snake_length-1]]=0;//蛇走过一格,蛇尾处变为空格
for(i=snake_length-1;i>0;i--)
{
snake_x[i]=snake_x[i-1]; //走过一格后蛇身的坐标覆盖前面的坐标 (从后往前覆盖,蛇头也会被蛇身覆盖)
snake_y[i]=snake_y[i-1];
map[snake_x[i]][snake_y[i]]=1;
}
}
if(primeinput=='w')
snake_x[0]--;
else if(primeinput=='s')
snake_x[0]++;
else if(primeinput=='a')
snake_y[0]--;
else if(primeinput=='d')
snake_y[0]++;
map[snake_x[0]][snake_y[0]]=4; //蛇头的坐标覆盖前面的坐标
}
int main()
{
setdata();//数据初始化
cout<<"请输入移动一格的毫秒数";
cin>>op;
while(1)
{
show(); //画面打印
Sleep(op);
updatewithoutinput(); //与输入无关的更新
updatewithinput(); //与输入有关的更新
}
return 0;
}
参考网站:https://blog.csdn.net/qq_38499859/article/details/70761558