#废话当头
首先膜拜CSDN上的各位前辈大佬们一下,本人写博客的原因只是想记录一下自己的成长。
贪吃蛇是我大一上学期的一个结课设计。想必大多数初入大学的萌新们在写贪吃蛇的时候都会感觉无从下手吧。
现在大家不用紧张,不用害怕了本人将会以一个菜鸟的视角教大家如何用数组写一个贪吃蛇。
#涉及函数
头文件 | 函数名 | 解释 |
---|---|---|
stdio.h | printf() | 在黑框框上打印文字用的 |
scanf() | 从键盘上输入文字所用 | |
stdlib.h | rand() | 获得随机的一个数值 |
conio.h | kbhit() | 判断当前键盘是否有输入 |
windows.h | system | 后面起到清屏变色的功能 |
Sleep() | 起到延迟的作用 |
以上是我感觉在写贪吃蛇的时候用到频率比较多的函数。实现光标移动的函数我找了很多也不知道是包含于哪一个头文件的就没有写在里面。
#如何编写
首先我们要做贪吃蛇这个游戏。首先解决让蛇运动这个最大的难题,在这里我们用的思维就是消失一个出现一个。就是在蛇头出现一个的时候蛇尾就相对应的消失一个。就这样消失出现消失出现。连续起来蛇就移动了起来。这个道理很容易明白那么如何编写呢?下面代码奉上。
void move(int x,int y)
{
COORD coord;//这一行里面的coord可以随意改变
coord.X=x;//这行里面的coord和上一行的必须一致下一行同理
coord.Y=y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),coord);//此行coord同理但是这一行其余的字母一个都不能错否则光标将不会移动到相应的坐标上
}
这一段的代码起到一个将光标移动到相应位置的作用。定义了一个新的函数move(int x,int y)形参x,y对应的就是横坐标和纵坐标。当我们分别给x,y一个值得时候,光标就移动到相对应的位置。
void down()//改变储存位置数组的数值
{
while(kbhit()==0)
{
head.a++;
head.y[head.a]=head.y[head.a-1]+1;//这里将上一个数组中储存的纵坐标数值加一并赋给下一个元素
head.x[head.a]=head.x[head.a-1];//因为我们接收到的是纵坐标的移动指令所以横坐标不变赋给下一个元素
pp.b++;
pp.x[pp.b]=head.x[head.a-length];//这里定义了蛇尾部,当然如果不进行定义也是可以的。
pp.y[pp.b]=head.y[head.a-length];
common();//这是本人自己定义的一个函数在后面会进行解释。
}
}
上面的代码块就是向下移动的代码。主要是数组中元素的连续赋值。然后进行光标的移动移动到相应的位置中就可以达到移动蛇的效果。
void shead()//打印新的蛇头
{
move(head.x[head.a],head.y[head.a]);
printf("■");
}
void spp()//抹除蛇的尾巴
{
move(pp.x[pp.b],pp.y[pp.b]);
printf(" ");
}
上面的代码块就起到了新增和抹除的作用,move()函数进行移动光标然后利用printf()函数进行打印或者抹除。
到这里我们就可以实现一个移动的小方块的程序。
那么新的问题来了,我们做出来的仅仅是一个移动的小方块,并无法做出来蛇那么我们怎样才能做出一“条”蛇呢?
pp.x[pp.b]=head.x[head.a-length];
我们可以通过引入一个变量length,通过这个变量我们就可以通过一个“差”来实现蛇身的出现。
void foods()//随机生成食物的函数
{
int x;
food.x=rand()%(52-4+1)+4;//控制生成数的区间使得生成的随机数在地图里面
food.y=rand()%(20-4+1)+4;
if(food.x%2==1)
{
foods();
}
else
{
for(x=head.a;x>=head.a-length;x--)
{
if(food.x==head.x[x]&&food.y==head.y[x])
{
foods();
}
}
}
move(food.x,food.y);
printf("■");
}
上年一段代码块实现的是给食取位置。在这里我们只需注意食物出现不在框框外不在蛇身上就可。其中给rand()函数取出的随机数设置范围即可实现食物不会跑没了。
通过一个递归函数就可保证出现的食物在横坐标上出现的坐标都为偶数。
for循环遍历对比食物是否会出现在蛇身上。
rand()%(a-b+1)+b控制随机数出现的大小大于b小于a。
以上就是本人在写贪吃蛇时最为崩溃的几个点。下面奉送完整的代码。(我尽量注释的详细一点)
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<conio.h>
#define UP 72//72就是大家按下↑这个的时候对应的键值
#define DOWN 80
#define LEFT 75
#define RIGHT 77
//************定义变量 大家尽量定义变量的时候具有一定的意义否则容易把自己搞昏头
char xy;//存储键盘敲下的字母
int s;//储存速度
void color();
struct food//定义食物的位置
{
int x;
int y;
}food;
struct pp
{
int x[2000];
int y[2000];
int b;
}pp;
struct head//定义蛇的头部
{
unsigned int x[2000];
unsigned int y[2000];
int a;
}head;
int length;//定义蛇身长
void speed();//通过对输入数字的判断改变蛇运动的速度
//************实现移动
void up();//上
void down();//下
void left();//左
void right();//右
//************基本流程
void original(); //初始化蛇身位置,打印边界
void GET();//蛇进行运动的判断语句
void shead();//蛇头移动
void spp();//蛇尾抹除
void foods(); //生成食物
void list();//显示参数
void gameopen();//欢迎界面
void gameover();//游戏结束
void common();
//************实现判断
void d_body();//判断body指令
void d_wall();//判断wall指令
void d_food();//判断food指令
void d_doot();//判断foot指令
//************
void move(int x,int y);//移动函数的声明语句
//************
int main()
{
gameopen();
}
void color()//实现界面变色的功能
{
if(length==5) system("color 0a");
if(length==10) system("color 0b");
if(length==20) system("color 0c");
if(length==30) system("color 0d");
if(length==40) system("color 0e");
if(length==50) system("color 0f");
}
void move(int x,int y)//个人理解为固定搭配其中coord可以被其他的替换其余的不可替换
{
COORD coord;
coord.X=x;
coord.Y=y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),coord);
}
void gameopen()
{
int x,y;
printf(" ******************************************************************\n");
printf(" * 说明: *\n");
printf(" * 1.w:上 s:下 a:左 d:右,与方向键共同控制蛇的移动方向 *\n");
printf(" * 2.选择1,2,3三种难度难度依次递增若未输入1,2,3则为最简单的模式 *\n");
printf(" * 3.若向移动方向的相反方向移动也为游戏结束 *\n");
printf(" * 4.在2000步内尽量吃到多的食物 *\n");
printf(" * 5.请在使用w,a,s,d移动时关闭中文输入法。 *\n");
printf(" * 6.游戏过程中按任意键暂停 *\n");
printf(" ******************************************************************");
printf("\n 按任意键继续。。。。。。");
getch();
system("cls");
move(29,10);
printf("选择难度回车开始游戏");
move(32,11);
scanf("%d",&s);
system("cls");
original();
}
void gameover()
{
//将游戏过程中产生的方块打印覆盖成空格。
int x,y;
move(0,0);
system("cls");
//下面将光标移动到特定的位置并且打印结束语,当按下一个按键时游戏重新开始。
move(35,12);
printf("结束游戏");
move(34,11);
printf("最终得分:%d",length-1);
move(31,13);
printf("按任意键重新开始");
getch();
move(35,12);
system("cls");
original();
}
void list()//主要起到显示蛇头坐标显示得分情况的作用
{
move(60,9);
printf("得分:%d",length-1);
move(60,10);
printf("步数:%d",head.a);
move(60,11);
printf("食物:x:%d y:%d",food.x,food.y);
move(60,12);
printf("蛇头:x:%d y:%d",head.x[head.a],head.y[head.a]);
}
void GET()//从键盘中获得一个键值,并将其赋值给xy,并且使用switch语句判断xy储存的值按照xy值得不同调用不用的函数
{
while(1)
{
xy=getch();
switch(xy)
{
case 'w':
{
up();
break;
}
case 's':
{
down();
break;
}
case 'a':
{
left();
break;
}
case 'd':
{
right();
break;
}
case 'W':
{
up();
break;
}
case 'S':
{
down();
break;
}
case 'A':
{
left();
break;
}
case 'D':
{
right();
break;
}
case UP:
{
up();
break;
}
case DOWN:
{
down();
break;
}
case LEFT:
{
left();
break;
}
case RIGHT:
{
right();
break;
}
}
}
}
void d_doot()//判断数组的使用情况。保证游戏的可玩性。
{
if(head.a==1999)
{
gameover();
}
}
void d_body()//判断蛇头和蛇身是否重合
{
int x;
for(x=head.a;x>head.a-length;x--)//通过for循环将蛇头的坐标与蛇身做判断
{
if(head.x[head.a]==head.x[x-1]&&head.y[head.a]==head.y[x-1])
{
gameover();
break;
}
}
}
void d_wall()//判断蛇头是否和墙体重合
{
if(head.x[head.a]==0||head.x[head.a]==56||head.y[head.a]==0||head.y[head.a]==24)
{
gameover();
}
}
void d_food()//判断蛇头和食物的坐标是否相同若相同蛇身加一
{
if(head.x[head.a]==food.x&&head.y[head.a]==food.y)
{
length+=1;//再吃下一个食物之后蛇身长加一,并且调用食物函数,产生一个新的食物
color();
foods();
}
}
void foods()//随机生成食物的函数
{
int x;
food.x=rand()%(52-4+1)+4;//控制生成数的区间使得生成的随机数在地图里面
food.y=rand()%(20-4+1)+4;
if(food.x%2==1)
{
foods();
}
else
{
for(x=head.a;x>=head.a-length;x--)
{
if(food.x==head.x[x]&&food.y==head.y[x])
{
foods();
}
}
}
move(food.x,food.y);
printf("■");
}
void original()//起到打印墙体并且给所有变量赋初值的作用。
{
length=1;
head.a=0;
pp.b=0;
int x,y;
for(x=0;x<56;x+=2)
{
y=0;
move(x,y);
printf("■");
}
for(y=0;y<24;y++)
{
move(x,y);
printf("■");
}
for(;x>0;x-=2)
{
move(x,y);
printf("■");
}
for(;y>0;y-=1)
{
move(x,y);
printf("■");
}
head.x[0]=12;
head.y[0]=12;
move(head.x[x],head.y[x]);
printf("■");
foods();
GET();
}
void up()//改变储存位置数组的数值
{
while(kbhit()==0)
{
head.a++;
head.y[head.a]=head.y[head.a-1]-1;
head.x[head.a]=head.x[head.a-1];
pp.b++;
pp.x[pp.b]=head.x[head.a-length];
pp.y[pp.b]=head.y[head.a-length];
common();
}
}
void down()//改变储存位置数组的数值
{
while(kbhit()==0)
{
head.a++;
head.y[head.a]=head.y[head.a-1]+1;
head.x[head.a]=head.x[head.a-1];
pp.b++;
pp.x[pp.b]=head.x[head.a-length];
pp.y[pp.b]=head.y[head.a-length];
common();
}
}
void left()//改变储存位置数组的数值
{
while(kbhit()==0)
{
head.a++;
head.x[head.a]=head.x[head.a-1]-2;
head.y[head.a]=head.y[head.a-1];
pp.b++;
pp.x[pp.b]=head.x[head.a-length];
pp.y[pp.b]=head.y[head.a-length];
common();
}
}
void right()//改变储存位置数组的数值
{
while(kbhit()==0)
{
head.a++;
head.x[head.a]=head.x[head.a-1]+2;
head.y[head.a]=head.y[head.a-1];
pp.b++;
pp.x[pp.b]=head.x[head.a-length];
pp.y[pp.b]=head.y[head.a-length];
common();
}
}
void speed()//判断开始时s中输入的数值并且使蛇头打印蛇尾抹除产生延迟
{
if(s==1)
{
Sleep(100);
}
else if(s==2)
{
Sleep(50);
}
else if(s==3)
{
Sleep(10);
}
else
{
Sleep(100);
}
}
void shead()//打印新的蛇头
{
move(head.x[head.a],head.y[head.a]);
printf("■");
}
void spp()//抹除蛇的尾巴
{
move(pp.x[pp.b],pp.y[pp.b]);
printf(" ");
}
void common()//将零碎的函数放在之中,简化代码的输入量。
{
spp();
shead();
d_food();
d_wall();
d_body();
d_doot();
list();
speed();
}
到这里就结束了,是在是不想写下去了,需要改进的地方大家可以告诉我。我会虚心改进的!!!