C语言结构数组实现贪吃蛇小游戏

飞来科技  发布时间:2019-08-09 02:03:41

本文关键词:c语言贪吃蛇实现

c语言贪吃蛇实现_贪吃蛇代码c语言_c语言贪吃蛇

一、设计思维

蛇身本质上就是个结构变量,函数里存储了坐标x、y的值,再通过一个循环把它打印出来,蛇的移动则是不断地刷新再次打印。所以撞墙、咬到自己只是函数x、y值的简单比较。

二、用上的知识点

结构变量Windows API函数

三、具体实现

先来实现静态网页,把地图、初始蛇身、食品搞定。

这里需要用到Windows API的常识,也就是对控制台上座标的改动

//这段代码来自参考1
void Pos(int x, int y) 
{ 
 COORD pos; 
 HANDLE hOutput; 
 pos.X = x; 
 pos.Y = y; 
 hOutput = GetStdHandle(STD_OUTPUT_HANDLE); 
 SetConsoleCursorPosition(hOutput, pos); 
} 

COORD是Windows API中定义的一种结构,认为在控制台上的座标

typedef struct _COORD { 
SHORT X; // horizontal coordinate 
SHORT Y; // vertical coordinate 
} COORD;

而源码中第七行则是获得屏幕缓冲区的函数,第八行是直接修改右键位置的变量。

1.地图。

有了Pos()变量,打印一个框就不是问题了。假如我们用"-"成为上下边框,把"|"成为左右边框,这看起来没怎么不妥,但毕竟我们早已坠入了坑里,直接上源码及实际效果图吧。

//LONG==60
//WIDTH==30
void CreateMap() 
{ 
 int i; 
 for(i=0;i<LONG;i++)//上下两行 
 { 
 Pos(i,1); 
 printf("-"); 
 Pos(i,WIDTH-1); 
 printf("-"); 
 } 
 for(i=2;i<WIDTH-1;i++)//左右两列 
 { 
 Pos(0,i); 
 printf("|"); 
 Pos(LONG-1,i); 
 printf("|"); 
 } 
} 

发现了难题吗?这是一条正常的蛇。。。那为什么看起来不正常呢?我们把边框都换成"#"来说说…

c语言贪吃蛇_c语言贪吃蛇实现_贪吃蛇代码c语言

这就清楚多了啊,要知道我们上下边框虽然各有60个"#"的,长60宽30的长方形输出之后居然成了个正方形。

原因在这

控制台上每位字节的长宽比例(长焦点)是不同的,但是就会出现上图这种蛋疼的现象。

解决办法同样也很简单,我们需要引入一些特定字符,譬如"●""■""⊙"等,这种符号的优点是它霸占两个普通符号的位置

如果上下边框就有60/2=30个符号,要让它一直是个正方形的话,左右也可以设为30(28+2)个符号.

源码及效果图如下

void CreateMap() 
{ 
 int i; 
 for(i=0;i<LONG;i+=2) 
 { 
 Pos(i,0); 
 printf("■"); 
 Pos(i,WIDTH-1); 
 printf("■"); 
 } 
 for(i=1;i<WIDTH-1;i++) 
 { 
 Pos(0,i); 
 printf("■"); 
 Pos(LONG-2,i); 
 printf("■"); 
 } 
} 

如此看就难受多了,但是也让复杂度提升了一些c语言贪吃蛇实现,上边框每个字符的座标分别是(,)(2,)(4,)…(2*n-2,)这个在蛇的移动及水果的系统再提。

2.初始化一条蛇

因为蛇还有食品 本质上都是一个坐标,但是我们可以定义一个新的数据类型Node,每一个Node都是一个存储了两个变量(x、y)的结构体,再借助Node来定义蛇和水果。

typedef struct node{ 
 int x; 
 int y; 
}Node; 
 
 
Node snake[60];

好了,我们现在定义了一条叫snake的蛇。为了这条蛇肥胖适中长宽比例一致,我们用"⊙"代表蛇的每一节。刚开始我们令蛇出现在地图右边位置,蟒蛇在右,共3个节点。所以我们需要求得每个结点的座标。

 void InitializeSnake() 
{ 
 int i; 
 for(i=0;i<3;i++) 
 { 
 snake[i].x = (LONG/2-i*2);//(30,15)(28,15)(26,15) 
 snake[i].y = WIDTH/2; 
 Pos(snake[i].x,snake[i].y); 
 printf("⊙"); 
 } 
} 

如此我们就在(30,15)(28,15)(26,15)三个坐标处确认了一条蛇。X坐标之间减2是因为"⊙"在X轴占两个基本值。

3.随机出现水果

c语言贪吃蛇_贪吃蛇代码c语言_c语言贪吃蛇实现

先创建一个变量来存储食物的座标

Nodefood;

得到它的座标其实就是用随机值对长、宽取余,使值在区间(地图)范围内。

void CreateFood() 
{ 
 int i; 
 srand((unsigned int)time(0)); 
 while(1) 
 { 
 do{ 
  food.x = rand()%(LONG-6)+2; 
 }while(food.x%2!=0); 
 food.y = rand()%(WIDTH-2)+1; 
 for(i=0;i<3+length;i++) 
  if(food.x==snake[i].x && food.y==snake[i].y) 
  { 
  i=-1; 
  break; 
  } 
 if(i>=0) 
 { 
  Pos(food.x,food.y); 
  printf("●"); 
  break; 
 } 
 } 
 //AfterEatFood(); 
} 

X的座标值求法为rand()%(LONG-6)+2,因为饮食"●"作为两个字符的位置,但是它应该的取值为(2,y)(4,y)…(56,y)上下变宽共30个字符,从开始,每位+2,但是最后一个为(58,y)

Rand()%(LONG)的取值范围为~59而x=1,x=2,x=58,x=59是地图范围,但是得对LONG-6(60-6=54)取余,这种取值范围就是~54,再加2,就成了2~56.又因为蛇的各节坐标及移动x坐标都是+2,但是饮食的x坐标必须是偶数,这可以用一个do(…)while()搞定,先取值,再分析,不行就再取值

Y的座标稍微简单些,只要确保坐标值在1~28就行。

另外求出了坐标之后要分析食品是否与蟒蛇重合,重叠的话再次赋值。

搞完下面的,我们就有了一个基本的(静态)作用了,现在我们要让它动起来

注:第86行是增设控制台窗口长、宽的功能变量。

4.让蛇动起来

蛇每次移动背后发生的事就是数组里的值改变,再在每位坐标位置打印蛇身。

为了让蛇始终动,我们就需要一个循环

while(1) 
{ 
 //获得输入,改变坐标 
 //在每个坐标处输出 
} 

首先,我们需要确认方向,而这需要两个变量c语言贪吃蛇实现,一个是输入值(应该是任意值),另一个则是确认方向的函数。

c语言贪吃蛇实现_c语言贪吃蛇_贪吃蛇代码c语言

这里介绍一个函数

int kbhit(void); 
// 检查当前是否有键盘输入,若有则返回一个非0值,否则返回0

这是一个非阻塞函数,有键按下时前往非,但此时键盘码一直在鼠标缓冲队列中。所以在确认键盘有响应以后,再用一个char数组将输入从缓冲区中调出来。

if(kbhit()) 
 ch = getch(); 

再对ch做判断,所以是合乎情况(不能往后走等)的输入,则直到执行switch改变坐标

if(ch=='w'&&direction!='s') 
 direction = ch; 
else if(ch=='s'&&direction!='w') 
 direction = ch; 
else if(ch=='a'&&direction!='d') 
 direction = ch; 
else if(ch=='d'&&direction!='a') 
 direction = ch; 
else if(ch==' ') 
 continue; 

这里设置空格是暂停,而为了让蛇一开始就移动,我们把direction设置为d(往右)。

在方向确定了以后,再用一个switch语句进行坐标判断

switch(direction) 
{ 
 case 'w': 
 if(snake[0].x==food.x && snake[0].y-1==food.y) 
 { 
  length++; 
  score+=10; 
  snake[2+length].x = snake[2+length-1].x; 
  snake[2+length].y = snake[2+length-1].y; 
  for(i=length+3-2;i>0;i--) 
  { 
  snake[i].x = snake[i-1].x; 
  snake[i].y = snake[i-1].y; 
  } 
  CreateFood(); 
 } 
 else 
 { 
  Pos(snake[2+length].x,snake[2+length].y); 
  printf(" "); 
  for(i=length+3-1;i>0;i--) 
  { 
  snake[i].x = snake[i-1].x; 
  snake[i].y = snake[i-1].y; 
  } 
 } 
 snake[0].y -=1; 
 break; 
 case 's': 
 //。。。 
 case 'a': 
 //。。。 
 case 'd': 
 //。。。 
} 

对巨蟒的下一步做判断,所以吃到了水果的话,则先对成绩等全局函数进行处理,再把snake[2+length-1](吃到食品后的倒数第二个变量)的值形参给snake[2+length](此时新加的尾节)。

再从倒数第二节开始,把前一节的座标值赋给后一节,直到第二节得到了之前蛇头坐标。在饮食被吃了以后,再读取随机出现水果函数。

因为没有吃到食品的话,先到之前最后一节的座标处,输入标点,算是销毁它再对各节再次赋值。在蟒蛇后每节都赋值完成以后,按照输入值单独对巨蟒赋值,如输入是'w',则往上,但是小蛇纵坐标减一。

对其他输入应该同样的道理,在snake函数各值都更新以后,再用一个函数把它打印出来。

如此移动部分就实现了,现在只需处理一些小系统就行。

5.移动后的处理。

c语言贪吃蛇_贪吃蛇代码c语言_c语言贪吃蛇实现

这一部分相对简单,即对分析蛇是否撞墙、是否咬到本身,再对这些现象做处理,我们用两个函数搞定它

int ThroughWall() 
{ 
 if(snake[0].x==0 || snake[0].x==58 || 
 snake[0].y==0 || snake[0].y==29) 
 { 
  Pos(25,15); 
  printf("撞墙 游戏结束"); 
  return 1; 
 } 
 Pos(0,WIDTH); 
 printf(" "); 
} 

int BiteItself() 
{ 
 int i; 
 for(i=3;i<=2+length;i++) 
 if((snake[0].x==snake[i].x) && (snake[0].y==snake[i].y)) 
 { 
  Pos(25,15); 
  printf("咬到自己 游戏结束"); 
  return 1; 
 } 
} 

当返回值为1时,网游也就GG了。

if(ThroughWall()==1) 
{ 
 Pos(25,WIDTH); 
 system("pause"); 
 exit(0); 
} 
if(BiteItself()==1) 
{ 
 Pos(25,WIDTH); 
 system("pause"); 
 exit(0); 
}

最终再加一行Sleep()变量,对刷新时间(每次重新打印的时间间隔)做处理。speed是一个变量,在每次吃到食品后递增。

Sleep(speed);

源代码在这:结构变量实现_贪吃蛇源码

四、总结与反思。

首先从蛇的结构上来说,结构变量的实现直接无视了"效率"这个词,函数占用较多空间且有体积限制,并不是一种好方法。

首先是BUG的难题,在ThroughWall()变量中,在对巨蟒坐标进行判断时在蟒蛇移动到(x,1)位置时,网游直接结束,且没有任何提示。

但奇怪的是,在分析后加入 Pos(,WIDTH);printf(" "); 这两行不相干的句子后,这个难题解决了,而我对这两行语句的原有目的则只是想把闪耀不停光标放在地图外面去。

还有就是while()循环里代码行很多,尤其是switch-case 里各项,蟒蛇的移动(结构变量个元素坐标值的变换)也是抽象成一个move()变量。

五、其它。

这是对我第一份代码(snakeV1.0)的变革,流程结构上有较大变化

重构期间研究了函数实现_贪吃蛇源码,在结构上采用了上面的部份精神。

个人空空如也的github:MagicXyxxx的github

以下就是本文的全部内容,能够对你们的学习有所帮助,也能够你们多多支持脚本之家。

本文来自互联网,由机器人自动采编,文章内容不代表本站观点,请读者自行辨别信息真伪,如有发现不适内容,请及时联系站长处理。

    时时彩平台