上节我们用方向控制函数写了个小画图程序,它虽然简单好玩,但我们不应该止步于此。革命尚未成功,同志还需努力。

开始撸代码之前,我们先理清一下思路。和前面画图程序不同,贪吃蛇可以有很多节,可以用一个足够大的结构体数组来储存它。 还需要一个食物坐标。定义如下:

typedef struct Position  //坐标结构
{
int x;
int y;
}Pos; Pos array; //移动方向向量
Pos snake[] = {}; //蛇的结构体数组,谁能够无聊到吃299999个食物~_~
long len=1; //蛇的长度
Pos egg; //食物坐标

之前的画图程序是四个方向都可以走,可蛇是不能倒着走的,所以方向控制函数要改成这样:

void command()                              //获取键盘命令
{
if (_kbhit()) //如果有键盘消息
switch (_getch()) /*这里不能用getchar()*/
{
case 'a':
if (array.x != || array.y != ) {//如果命令不是倒着走,就修正方向向量,否则不做改变,下同。
array.x = -;
array.y = ;
}
break;
case 'd':
if (array.x != - || array.y != ) {
array.x = ;
array.y = ;
}
break;
case 'w':
if (array.x != || array.y != ) {
array.x = ;
array.y = -;
}
break;
case 's':
if (array.x != || array.y != -) {
array.x = ;
array.y = ;
}
break;
}
}

蛇可能不止一节,所以移动函数需要做出改变。仔细一想就知道,走了一步之后,除了头结点外,每个节点的下一个坐标为它前一个结点之前的坐标,而头节点的坐标等于它本身坐标加上移动向量(这里是 方向向量*10)

还有个问题是蛇走过的痕迹需要擦除,每走一步,它留下的痕迹应该是走这一步之前蛇的最末一个结点的坐标,我们需要擦除掉它。

结果如下:

void move()    //修改各节点坐标以达到移动的目的
{
setcolor(BLACK); //覆盖尾部走过的痕迹
rectangle(snake[len-].x - , snake[len-].y - , snake[len-].x + , snake[len-].y + ); for (int i = len-; i >; i--) //除了头结点外,每个节点的下一个坐标为它前一个结点当前的坐标
{
snake[i].x = snake[i - ].x;
snake[i].y = snake[i - ].y;
}
snake[].x += array.x*; //头节点的坐标等于它本身坐标加上移动向量(这里是 方向向量*10)
snake[].y += array.y*;
}

另外,我们的蛇是有穿墙术的~~~它的实现方法非常简单:

void break_wall()
{
if (snake[].x >= ) //如果越界,从另一边出来
snake[].x = ;
else if (snake[].x <= )
snake[].x = ;
else if (snake[].y >= )
snake[].y = ;
else if (snake[].y <= )
snake[].y = ;
}

接下来是食物相关函数,这个算是重点。

1. 食物生成

我们希望食物每次出现的位置都是随机的, 可以这样实现。

         srand((unsigned)time(NULL));
egg.x = rand() % * + ; //头节点位置随机化
egg.y = rand() % * + ;

而且食物不能与蛇重合,最好也不要离蛇太近。综合起来就是这样:(srand在初始化中会被调用,所以这里略去了)

void creat_egg()
{
while (true)
{
int ok = ; //这是个标记,用于判断函数是否进入了某一分支
egg.x = rand() % * + ; //头节点位置随机化
egg.y = rand() % * + ;
for (int i = ; i < len; i++) //判断是否离蛇太近
{
if (snake[i].x == && snake[i].y == )
continue;
if (fabs(snake[i].x - egg.x) <= && fabs(snake[i].y - egg.y) <= )
ok = -; //如果,进入此分支,改变标记
break;
}
if (ok == ) //如果不重合了,跳出函数
return;
}
}

2. 吃到食物

如果吃到食物,那么需要消除被吃掉的食物,生成新食物,蛇也要增长一节。

我觉得这里最麻烦的就是蛇变长的实现:是在蛇头添加一节,还是在蛇尾?添加在蛇头(尾)的上下左右哪一边?

想来想去,只有在蛇头位置,我们可以根据当前方向向量,在移动方向上新添一节。这对应的代码如下:

        //add snake node
len += ;
for (int i = len - ; i > ; i--) //所有数据后移一个单位,腾出snake[0]给新添的一节
{
snake[i].x = snake[i - ].x;
snake[i].y = snake[i - ].y;
}
snake[].x += array.x * ; //这就是新添的这一节的位置
snake[].y += array.y * ;

吃到食物的完整代码如下:

void eat_egg()
{
if (fabs(snake[].x - egg.x) <= && fabs(snake[].y - egg.y) <= ) //判断是否吃到食物,因为食物位置有点小偏差,只好使用范围判定~~
{
setcolor(BLACK); //hide old egg
circle(egg.x, egg.y, );
creat_egg(); //create new egg
//add snake node
len += ;
for (int i = len - ; i >; i--)
{
snake[i].x = snake[i - ].x;
snake[i].y = snake[i - ].y;
}
snake[].x += array.x * ; //每次移动10pix
snake[].y += array.y * ;
}
}

游戏结束判定

最后,我们还差一个死亡判定,因为自带穿墙术,所以实际的死亡判定只有一个,就是咬到自己,代码如下:

void eat_self()
{
if (len == ) //只有一节当然吃不到自己~~
return;
for (int i = ; i < len; i++)
if (fabs(snake[i].x - snake[].x) <= && fabs(snake[i].y - snake[].y) <= ) //如果咬到自己(为了不出bug,使用了范围判定)
{
outtextxy(, , "GAME OVER!"); //你的蛇死了~
Sleep(); //3s时间让你看看你的死相~~
closegraph();
exit(); //退出
}
}

当然,你也可以直接丢掉这个函数,然后开心地狂咬自己—_—||

最后:画图函数

画出食物和蛇,其实蛇没必要全部画出来,只要画蛇头就可以了,但这之中有些小问题,谁有兴趣可以自己玩玩,我是懒得动了~

void draw()     //画出蛇和食物
{
setcolor(BLUE);
for (int i = ; i < len; i++)
{
rectangle(snake[i].x - , snake[i].y - , snake[i].x + , snake[i].y + );
}
setcolor(RED); //画蛋(怎么感觉怪怪的~)
circle(egg.x, egg.y, );
Sleep();
}

到这里,游戏大功告成~~  什么?你说运行不起来?那是因为少了初始化函数,和游戏循环啦~~这几个都比较简单,就直接放下面了:

void init()              //初始化
{
initgraph(, ); //初始化图形界面
srand((unsigned)time(NULL)); //初始化随机函数
snake[].x = rand() % * + ; //头节点位置随机化
snake[].y = rand() % * + ;
array.x = pow(-,rand()); //初始化方向向量,左或者右
array.y = ;
creat_egg();
} int main()
{
init();
while (true)
{
command(); //获取键盘消息
move(); //修改头节点坐标-蛇的移动
eat_egg();
draw(); //作图
eat_self();
} return ;
}

好了,这是真的大功告成了。给你们看看死亡方式之自尽:

完整代码如下:

 #include<graphics.h>
#include<conio.h>
#include<time.h>
#include<math.h> typedef struct Position //坐标结构
{
int x;
int y;
}Pos; Pos snake[] = {};
Pos array;
Pos egg;
long len=; void creat_egg()
{
while (true)
{
int ok = ;
srand((unsigned)time(NULL)); //初始化随机函数
egg.x = rand() % * + ; //头节点位置随机化
egg.y = rand() % * + ;
for (int i = ; i < len; i++)
{
if (snake[i].x == && snake[i].y == )
continue;
if (fabs(snake[i].x - egg.x) <= && fabs(snake[i].y - egg.y) <= )
ok = -;
break;
}
if (ok == )
return;
}
} void init() //初始化
{
initgraph(, ); //初始化图形界面
srand((unsigned)time(NULL)); //初始化随机函数
snake[].x = rand() % * + ; //头节点位置随机化
snake[].y = rand() % * + ;
array.x = pow(-,rand()); //初始化方向向量
array.y = ;
creat_egg();
} void command() //获取键盘命令
{
if (_kbhit()) //如果有键盘消息
switch (_getch()/*这里不能用getchar()*/)
{
case 'a':
if (array.x != || array.y != ) {//如果不是反方向
array.x = -;
array.y = ;
}
break;
case 'd':
if (array.x != - || array.y != ) {
array.x = ;
array.y = ;
}
break;
case 'w':
if (array.x != || array.y != ) {
array.x = ;
array.y = -;
}
break;
case 's':
if (array.x != || array.y != -) {
array.x = ;
array.y = ;
}
break;
}
} void move() //修改各节点坐标以达到移动的目的
{
setcolor(BLACK); //覆盖尾部走过的痕迹
rectangle(snake[len-].x - , snake[len-].y - , snake[len-].x + , snake[len-].y + ); for (int i = len-; i >; i--)
{
snake[i].x = snake[i - ].x;
snake[i].y = snake[i - ].y;
}
snake[].x += array.x*; //每次移动10pix
snake[].y += array.y*; if (snake[].x >= ) //如果越界,从另一边出来
snake[].x = ;
else if (snake[].x <= )
snake[].x = ;
else if (snake[].y >= )
snake[].y = ;
else if (snake[].y <= )
snake[].y = ;
} void eat_egg()
{
if (fabs(snake[].x - egg.x)<= && fabs(snake[].y - egg.y)<=)
{
setcolor(BLACK); //shade old egg
circle(egg.x, egg.y, );
creat_egg();
//add snake node
len += ;
for (int i = len - ; i >; i--)
{
snake[i].x = snake[i - ].x;
snake[i].y = snake[i - ].y;
}
snake[].x += array.x * ; //每次移动10pix
snake[].y += array.y * ;
}
} void draw() //画出蛇和食物
{
setcolor(BLUE);
for (int i = ; i < len; i++)
{
rectangle(snake[i].x - , snake[i].y - , snake[i].x + , snake[i].y + );
}
setcolor(RED);
circle(egg.x, egg.y, );
Sleep();
} void eat_self()
{
if (len == )
return;
for (int i = ; i < len; i++)
if (fabs(snake[i].x - snake[].x) <= && fabs(snake[i].y - snake[].y) <= )
{
Sleep();
outtextxy(, , "GAME OVER!");
Sleep();
closegraph();
exit();
}
} int main()
{
init();
while (true)
{
command(); //获取键盘消息
move(); //修改头节点坐标-蛇的移动
eat_egg();
draw(); //作图
eat_self();
} return ;
}

snakey

可能还有若干bug留存,欢迎大家指正~~

甲铁城镇~

贪吃蛇—C—基于easyx图形库(下):从画图程序到贪吃蛇【自带穿墙术】的更多相关文章

  1. 贪吃蛇—C—基于easyx图形库(上):基本控制函数实现 画图程序

    自从学了c语言,就一直想做个游戏,今天将之付之行动,第一次写的特别烂,各种bug:就不贴了.今天网上看了好几个贪吃蛇,重新写了一次,做出来的效果还可以. p.s.  easyx图形库是为了方便图形学教 ...

  2. 基于EasyX库的贪吃蛇游戏——C语言实现

    接触编程有段时间了,一直想学习怎么去写个游戏来练练手.在看了B站上的教学终于可以自己试试怎么实现贪吃蛇这个游戏了.好了,废话不多说,我们来看看如何用EasyX库来实现贪吃蛇. 一.准备 工具vc++6 ...

  3. easyx图形库做贪吃蛇游戏

    编程总是对着一个黑窗口,可以说是非常乏味了,于是喵喵就翻出来了以前用easyx图形库做图形界面的贪吃蛇游戏. 不过大家只是当做提高编程的乐趣来学习吧,想进一步做的话可以学习QT,还有其他的框架. 这是 ...

  4. 基于jQuery向下弹出遮罩图片相册

    今天给大家分享一款基于jQuery向下弹出遮罩图片相册.单击相册图片时,一个遮罩层从上到下动画出现.然后弹出显示图片.这款插件适用浏览器:IE8.360.FireFox.Chrome.Safari.O ...

  5. 一款基于jquery的下拉点击改变背景图片

    今天给大家介绍一款基于jquery的下拉点击改变背景图片.单击右上角的图片,下拉显示可选择的背景图片,单击图片变为背景图.效果图下: 在线预览   源码下载 实现的代码. html代码: <a ...

  6. 基于Windows环境下cmd/编译器无法输入中文,显示中文乱码解决方案

    基于Windows环境下cmd/编译器无法输入中文,显示中文乱码解决方案 两个月前做C++课设的时候,电脑编译器编译结果出现了中文乱码,寻求了百度和大神们,都没有解决这个问题,百度上一堆解释是对编译器 ...

  7. 基于jsmpeg库下使用ffmpeg创建视频流连接websocket中继器传输视频并播放

    这个功能的基本工作是这样的: 1.使用node运行jsmpeg库下的websocket-relay.js文件,这个文件的作用是创建一个websocket视频传输中继器 2.运行ffmpeg,将输出发送 ...

  8. 【申嵌视频】基于VMWare虚拟机下安装ubuntu操作系统的详细步骤

    [申嵌视频]基于VMWare虚拟机下安装ubuntu操作系统 适合搭建mini2440, Tiny6410, smart210,Tiny4412, NanoPC-T2, NanoPC-T3, Nano ...

  9. 基于jQuery select下拉框美化插件

    分享一款基于jQuery select下拉框美化插件.该插件适用浏览器:IE8.360.FireFox.Chrome.Safari.Opera.傲游.搜狗.世界之窗.效果图如下: 在线预览   源码下 ...

随机推荐

  1. HDU5152 线段树 + 数论

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5152 ,线段树区间更新 + 点更新 + 数论知识(数论是重点QAQ),好题值得一做. BestCode ...

  2. V2EX 神回复 #1

    "抠图"用英文怎么说 今天突然被"抠图"这个单词给难住了," image segmentation "," image cut & ...

  3. Spark性能优化——和shuffle搏斗

    Spark的性能分析和调优很有意思,今天再写一篇.主要话题是shuffle,当然也牵涉一些其他代码上的小把戏. 以前写过一篇文章,比较了几种不同场景的性能优化,包括portal的性能优化,web se ...

  4. OO2019第四单元作业总结

    一.本单元两次作业的架构设计  1.第一次作业 第一次作业由于时间仓促,没有过多的架构设计,就直接补全了所给的MyUmlInteraction类,导致整个程序的代码风格和效率都不高,在强测中也因此失掉 ...

  5. 使用U盘引导安装CentOS

    一.制作linux引导盘 1. 格式化U盘:格式成FAT32格式 2. 安装syslinux https://www.kernel.org/pub/linux/utils/boot/syslinux/ ...

  6. Java 程序设计总复习题

    Java程序设计总复习题 1.编写一个Java程序在屏幕上输出“你好!”. //programme name Helloworld.java public class Helloworld { pub ...

  7. oc数组遍历

    #import <Foundation/Foundation.h> //数组遍历(枚举)对集合中的元素依此不重复的进行遍历 int main(int argc, const char * ...

  8. 2018.11.7 Nescafe29 T1 穿越七色虹

    题目 题目背景 在 Nescafe27 和 28 中,讲述了一支探险队前往 Nescafe 之塔探险的故事…… 当两位探险队员以最快的时间把礼物放到每个木箱里之后,精灵们变身为一缕缕金带似的光,簇簇光 ...

  9. CentOS6 x86_64最小化安装优化脚本

    #!/bin/bash #centos6. x86_64系统最小化安装优化脚本 #系统基础优化,建议以root运行 if [ $USER != "root" ];then echo ...

  10. JS大小转化B KB MB GB的转化方法

    function conver(limit){ var size = ""; ){ //如果小于0.1KB转化成B size = limit.toFixed() + "B ...