贪吃蛇—C—基于easyx图形库(下):从画图程序到贪吃蛇【自带穿墙术】
上节我们用方向控制函数写了个小画图程序,它虽然简单好玩,但我们不应该止步于此。革命尚未成功,同志还需努力。
开始撸代码之前,我们先理清一下思路。和前面画图程序不同,贪吃蛇可以有很多节,可以用一个足够大的结构体数组来储存它。 还需要一个食物坐标。定义如下:
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图形库(下):从画图程序到贪吃蛇【自带穿墙术】的更多相关文章
- 贪吃蛇—C—基于easyx图形库(上):基本控制函数实现 画图程序
自从学了c语言,就一直想做个游戏,今天将之付之行动,第一次写的特别烂,各种bug:就不贴了.今天网上看了好几个贪吃蛇,重新写了一次,做出来的效果还可以. p.s. easyx图形库是为了方便图形学教 ...
- 基于EasyX库的贪吃蛇游戏——C语言实现
接触编程有段时间了,一直想学习怎么去写个游戏来练练手.在看了B站上的教学终于可以自己试试怎么实现贪吃蛇这个游戏了.好了,废话不多说,我们来看看如何用EasyX库来实现贪吃蛇. 一.准备 工具vc++6 ...
- easyx图形库做贪吃蛇游戏
编程总是对着一个黑窗口,可以说是非常乏味了,于是喵喵就翻出来了以前用easyx图形库做图形界面的贪吃蛇游戏. 不过大家只是当做提高编程的乐趣来学习吧,想进一步做的话可以学习QT,还有其他的框架. 这是 ...
- 基于jQuery向下弹出遮罩图片相册
今天给大家分享一款基于jQuery向下弹出遮罩图片相册.单击相册图片时,一个遮罩层从上到下动画出现.然后弹出显示图片.这款插件适用浏览器:IE8.360.FireFox.Chrome.Safari.O ...
- 一款基于jquery的下拉点击改变背景图片
今天给大家介绍一款基于jquery的下拉点击改变背景图片.单击右上角的图片,下拉显示可选择的背景图片,单击图片变为背景图.效果图下: 在线预览 源码下载 实现的代码. html代码: <a ...
- 基于Windows环境下cmd/编译器无法输入中文,显示中文乱码解决方案
基于Windows环境下cmd/编译器无法输入中文,显示中文乱码解决方案 两个月前做C++课设的时候,电脑编译器编译结果出现了中文乱码,寻求了百度和大神们,都没有解决这个问题,百度上一堆解释是对编译器 ...
- 基于jsmpeg库下使用ffmpeg创建视频流连接websocket中继器传输视频并播放
这个功能的基本工作是这样的: 1.使用node运行jsmpeg库下的websocket-relay.js文件,这个文件的作用是创建一个websocket视频传输中继器 2.运行ffmpeg,将输出发送 ...
- 【申嵌视频】基于VMWare虚拟机下安装ubuntu操作系统的详细步骤
[申嵌视频]基于VMWare虚拟机下安装ubuntu操作系统 适合搭建mini2440, Tiny6410, smart210,Tiny4412, NanoPC-T2, NanoPC-T3, Nano ...
- 基于jQuery select下拉框美化插件
分享一款基于jQuery select下拉框美化插件.该插件适用浏览器:IE8.360.FireFox.Chrome.Safari.Opera.傲游.搜狗.世界之窗.效果图如下: 在线预览 源码下 ...
随机推荐
- Extjs4.1+desktop+SSH2 定义程序入口
app.js定义程序入口: MainController.js: 加载控制器: 外部组件引用入口loader.js 时间组件 静态变量组件: 引入comm.js index.jsp 验证打印 comm ...
- String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getSer
这其实就是 获得应用的根url,比如说你的应用的根路径是 http://localhost:8080,那么你列出的代码就是为basePath赋值为 http://localhost:8080.具体点: ...
- python_26_dictionary
#key-value 字典无下标 所以乱序,key值尽量不要取中文 info={ 'stu1101':'Liu Guannan', 'stu1102':'Wang Ruipu', 'stu1103': ...
- 02-CSS基础与进阶-day13_2018-09-21-20-05-21
css3动画 @keyframes 动画名 { 0% { } 100% { } } 元素执行动画 animation: 动画名 运动时间 运动曲线 01运动的汽车.html <!DOCTYPE ...
- jquery循环获取name相同的元素
今天做项目时,用到一种用jquery循环获取name相同的按钮,并且完成点击事件,记录一下 首先整段的html,是用js拼出来的(项目需求) getStudentArticle:function(op ...
- Bootstrap 历练实例-轮播(carousel)插件的事件
事件 下表列出了轮播(Carousel)插件中要用到的事件.这些事件可在函数中当钩子使用. 事件 描述 实例 slide.bs.carousel 当调用 slide 实例方法时立即触发该事件. $(' ...
- Excel坐标点转线
IWorkspaceFactory pShpWksFact = new ShapefileWorkspaceFactory(); IFeatureWorkspace pFeatWks; pFeatWk ...
- Jquery的简单API
dsfsdjgsdjgsdjkg <script>console.log('erftwet')</script>
- 文档处理jQuery,实现添加删除复制
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 三、MySQL PHP 语法
MySQL PHP 语法 MySQL 可应用于多种语言,包括 PERL, C, C++, JAVA 和 PHP. 在这些语言中,Mysql在PHP的web开发中是应用最广泛. 在本教程中我们大部分实例 ...