贪吃蛇(⼀)--⽤C++编写⼀个简单的贪吃蛇
这⾥简单介绍怎么⽤C++编写⼀个简单的⿊⽩框的贪吃蛇游戏,复杂的加了可视化界⾯程序点击这⾥。
⾸先分析在⿊⽩框中的贪吃蛇需要哪些功能:
(1)需要能在界⾯指定位置(x,y)直接输出对应内容
(2)需要动态数组储存蛇的⾝体节点
(3)需要能接收键盘指令对贪吃蛇运动⽅向进⾏调整
c语言游戏贪吃蛇源码(4)需要随机⽣成⾷物
(5)判断蛇是否撞到墙或者⾃⼰的⾝体
基本满⾜这些功能就可以实现⼀个简单的贪吃蛇,但是为了游戏的稳定性我们需要尽可能考虑详细,避免程序出bug。现在依次对这5个功能进⾏实现。
1.在界⾯指定位置(x,y)输出对应内容
这⾥直接引⽤C语⾔本⾝存在的⼀个操作台函数,代码如下:
#include<iostream>
#include<windows.h>
using namespace std;
//在指定位置显⽰内容
void gotoxy(int x,int y,char c)
{
CONSOLE_SCREEN_BUFFER_INFO csbiInfo;//variablendklaration
HANDLE hConsoleOut;
hConsoleOut =GetStdHandle(STD_OUTPUT_HANDLE);
GetConsoleScreenBufferInfo(hConsoleOut,&csbiInfo);
csbiInfo.dwCursorPosition.X = x;//cursorposition X koordinate festlegen
csbiInfo.dwCursorPosition.Y = y;//cursorposition Y koordinate festlegen
SetConsoleCursorPosition(hConsoleOut,csbiInfo.dwCursorPosition);//den cursor an die festgelegte koordinate setzen
printf("%c",c);//输出你指定的字符
}
int main()
{
gotoxy(5,5,'*');//在坐标(5,5)的位置显⽰'*'
gotoxy(10,10,'#');//在坐标(10,10)的位置显⽰'#'
return0;
}
其中的gotoxy函数就是我们可以在指定位置输出指定字符的函数,其对应的库函数为"windows.h",上述代码运⾏结果为
于是我们趁热打铁,把贪吃蛇的围墙顺便画出来,围墙为⼀个矩形,⽽矩形的左上⾓和右下⾓端点坐标就可以确定这个矩形,于是我们只需要定义左上⾓和右下⾓坐标即可。
int X1=1,Y1=1;//活动范围的左上⾓坐标
int X2=60,Y2=30;//活动范围的右下⾓坐标
void init()//初始化函数
{
for(int i=X1;i<=X2;i++)
{
gotoxy(i,Y1,'#');
gotoxy(i,Y2,'#');
}
for(int j=Y1;j<=Y2;j++)
{
gotoxy(X1,j,'#');
gotoxy(X2,j,'#');
}
}
在主函数中运⾏初始化函数init()结果为
于是画出了⼀个简单的边框了。
2.动态数组储存蛇的⾝体节点
这⾥直接⽤stl库中的vector储存了吧,然后定义⼀个结构体表⽰蛇的⾝体节点,这样书写⽐较容易,嘿嘿,代码如下。
struct Snake
{
int x,y;
};
vector<Snake>snake;
然后库函数部分记得添加vector
#include<iostream>
#include<windows.h>
#include<vector>
using namespace std;
这就是定义简单的蛇节点的动态数组了,然后我们初始化让开始的蛇头在活动范围的中间位置,然后在主函数中⼀直循环显⽰出来。这⾥我让蛇先按朝着往下的⽅向移动,蛇的节点很多,那么如何让蛇移动呢?其实只需要改变蛇头位置就⾏,然后往后的节点更新到上⼀个节点的坐标位置,⽐如先把蛇头位置更新给第⼆个节点,蛇头位置再更新成我们调整的位置。
但是蛇移动后原来位置输出的内容是需要清除的,这⾥我需要补充说⼀下了,可能在很多其他的贪吃蛇的写法中,为了书写⽅便直接⽤了system(“cls”)清空了整个界⾯内容,再把新的界⾯信息输出,这样没问题,但是⿊⽩框不是专门⽤于可视化界⾯程序的,这样会让贪吃蛇程序看起来很卡顿,后期的可视化界⾯的贪吃蛇确实需要直接清空整个界⾯,但是现在的这个⿊⽩框的程序没必要,我为了减⼩程序时间复杂度,考虑到贪吃蛇实际每次运动都是两个节点在变化,蛇头和蛇尾,蛇头是新输出的内容不管,蛇尾移动后原来的蛇尾就需要清空,于是我们在每次更新蛇的节点信息前,先把原来的蛇尾的位置清除,⽤gotoxy(x,y,’ ');空格就会显⽰没有,看起来就是清除的作⽤。
然后到这⾥完整的代码就是这样了:
#include<iostream>
#include<windows.h>
#include<vector>
using namespace std;
//在指定位置显⽰内容
void gotoxy(int x,int y,char c)
{
CONSOLE_SCREEN_BUFFER_INFO csbiInfo;//variablendklaration
HANDLE hConsoleOut;
hConsoleOut =GetStdHandle(STD_OUTPUT_HANDLE);
GetConsoleScreenBufferInfo(hConsoleOut,&csbiInfo);
csbiInfo.dwCursorPosition.X = x;//cursorposition X koordinate festlegen
csbiInfo.dwCursorPosition.Y = y;//cursorposition Y koordinate festlegen
SetConsoleCursorPosition(hConsoleOut,csbiInfo.dwCursorPosition);//den cursor an die festgelegte koordinate setzen
printf("%c",c);//输出你指定的字符
}
int X1=1,Y1=1;//活动范围的左上⾓坐标
int X2=60,Y2=30;//活动范围的右下⾓坐标
struct Snake
{
int x,y;
};
vector<Snake>snake;
void init()//初始化函数
{
for(int i=X1;i<=X2;i++)
{
gotoxy(i,Y1,'#');
gotoxy(i,Y2,'#');
}
for(int j=Y1;j<=Y2;j++)
{
gotoxy(X1,j,'#');
gotoxy(X2,j,'#');
}
snake.clear();//初始化清空
Snake t;
t.x=(X1+X2)/2;
t.y=(Y1+Y2)/2;
snake.push_back(t);
}
//更新的位置,上下左右
int XX[4]={0,0,-1,1};
int YY[4]={-1,1,0,0};
void Print(int direction)//显⽰蛇的内容
{
int n=snake.size()-1;
gotoxy(snake[n].x,snake[n].y,' ');//先清除蛇尾留下的痕迹for(int i=n;i>=1;i--)
snake[i]=snake[i-1];//进⾏节点更新
snake[0].x+=XX[direction];
snake[0].y+=YY[direction];
for(int i=1;i<=n;i++)
gotoxy(snake[i].x,snake[i].y,'*');
gotoxy(snake[0].x,snake[0].y,'@');//蛇头⽤@表⽰
}
int main()
{
init();
while(true)
{
Print(1);
Sleep(500);//延迟500ms,控制程序显⽰时间
}
return0;
}
动态图显⽰如下
到这⾥看起来就有点意思了,我们继续⼲。
3.接收键盘指令对贪吃蛇运动⽅向进⾏调整
现在要根据我们输⼊的指令进⾏贪吃蛇运动的调整,我们的贪吃蛇运动是通过死循环+睡眠延时所以看起来在运动⼀样,那么怎么在循环过程中加⼊操作指令呢,这⾥⼜需要⽤到⼀个函数了,叫kbhit函数,这个函数是键盘监听函数,主要功能就是如果当前你按了键盘(不论什么键),这个函数就会返回true,表⽰有键盘指令,不然就是false,表⽰当前⽆操作。于是我们可以利⽤这个输⼊我们的操作指令。其次还需要⼀个函数,getch(),这个函数是输⼊单个字符,不⽤再按回车,因为我们按⽅向键后要求蛇马上按我们的⽅向去运动。
kbhit和getch函数都是在库函数conio.h中,注意添加库函数
#include<conio.h>
然后在主函数中进⾏的更改为
int main()
{
init();
int direction=1;
while(true)
{
Print(direction);
if(kbhit())//判断有键盘指令输⼊
{
char c=getch();
if(c=='W')//往上
direction=0;
else if(c=='S')//往下
direction=1;
else if(c=='A')//往左
direction=2;
else if(c=='D')//往右
direction=3;
}
Sleep(300);//延迟300ms,控制程序显⽰时间
}
return0;
}
我现在⼿动输⼊⽅向键“W”,“S”,“A”,"D"进⾏⼀定⽅向操作控制,运⾏结果如下。
这⾥备注下,因为截取的动态图没有完全显⽰,实际显⽰的内容@后⾯会有⼀个⽩⾊的光标,因为@是最后⼀个输出的内容,会跟着⼀个⽩⾊光标,为了美观,我们将⽩⾊光标移去其他位置,⽐如每次在界⾯外输出gotoxy(x,y,’ '),x和y是在这个墙的范围外的任意⼀点。
在主函数中添加如下
while(true)
{
Print(direction);
if(kbhit())//判断有键盘指令输⼊
{
char c=getch();
if(c=='W')//往上
direction=0;
else if(c=='S')//往下
direction=1;
else if(c=='A')//往左
direction=2;
else if(c=='D')//往右
direction=3;
}
gotoxy(X2+1,Y2+1,' ');//为了不在游戏界⾯中显⽰⽩⾊光标影响界⾯效果
Sleep(300);//延迟500ms,控制程序显⽰时间
}
4.需要随机⽣成⾷物
随机⽣成⾷物,也是整个程序最复杂的部分,你以为是简单的随机数⽣成吗?不是,确实需要随机⽣成,但是更⿇烦的在于,如果随机⽣成的位置恰好是蛇的⾝体部分,那么是不对的,重新再⽣成,但是如果蛇越来越长,那么就会有很⼤的概率⽣成的⾷物和蛇的⾝体位置冲突,就会导致⼀直循环判断⽣成⾷物不要和蛇的⾝体冲突,甚⾄会死循环,因为是随机⽣成的,你难免会每次都⽣成的⾷物位置恰好和蛇的⾝体都冲突了。
所以怎么随机⽣成⾷物,恰好⼜可以⾼效的到不会和蛇的⾝体冲突的位置呢?
于是我想到了⼀个有趣的⽅法,分治处理,我们的地图是⼆维的,但是可以转换成⼀维表⽰,这个没问题,然后我们针对给定的⼀维坐标范围[L,R]随机⽣成⼀个数字X,把X转换成⼆维坐标,如果这个X对应的⼆维坐标和蛇的⾝体冲突了,那么就⼆分处理,分成[L,X-1]和
[X+1,R]两个⼦区间,再分别在这两个⼦区间随机,⼀直这样下去,就可以很快到不会和蛇的⾝体冲突的坐标,递归处理,哈哈哈。
随机递归⽣成⾷物代码如下
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论