本文主要是介绍交互媒体技术应用——2D横版冒险游戏初尝试,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
2D横版冒险游戏——模拟人生《Just Die》
- 想法
- 背景
- 使用工具
- 参考资料
- 关键技术
- 小球的运动
- 碰撞检测
- 粒子系统
- 优化算法
- 粒子系统的预处理
- 从预处理的粒子系统中获取需要的子系统
- 其他
- 后记
- 附录
想法
最早是在B站看到一个视频,介绍了一个模拟人生的横版游戏,玩家需要操控游戏中的角色,根据不同的关卡做出不同的举动,来完成他的一生。简单但是很有深意,让我思考了很久,并在心里埋下了这个想法。这是一个就我个人而言代入感十分强烈的小Demo,包含我对这20年成长的一些思考,可能不有趣,而且由于代码能力不足,有一些糟糕的bug,导致游戏可玩性不强,以后应该会继续做,做出我满意的样子来。
背景
使用工具
processing
参考资料
《代码本色 The Nature of Code》 —— Daniel Shiffman
本Demo中参考的内容主要包含本书的前四章,向量、力、粒子系统等。
关键技术
小球的运动
将小球定义为包含位置(location)、速度(velocity)以及加速度(acceleration)的对象,根据加速度更新速度,根据速度更新位置,来实现小球的运动效果。这三个变量都存储在Processing自带的向量PVector中。
class people extends Collider {PVector velocity; //速度PVector acceleration; //加速度float topspeed=2; //最大速度float mass=10; //质量float c=0.001;//摩擦力系数people() {location=new PVector(10, height-10);velocity=new PVector(0, 0);acceleration=new PVector(0, 0);R=5.0;}people(PVector location_, PVector velocity_, PVector acceleration_, float R_) {location=location_.get();velocity=velocity_.get();acceleration=acceleration_.get();R=R_;}}
在Update中更新变量,并在display绘制实时图像:
void update() {applygravity();velocity.add(acceleration);location.add(velocity);velocity.limit(topspeed);acceleration=new PVector(0, 0);}void display() {fill(0);ellipse(location.x, location.y, 2*R, 2*R);}
这里面还包含了对力的应用addforce();我们知道,速度决定位置的变化,加速度决定速度的变化,而加速度来源于力,因此当我们想要改变一个物体的运动状态时,只需要给他加上一个力即可:
void applyForce(PVector force) {PVector f=force.get();f.div(mass);acceleration.add(f);}
还可以细分为许多不同类型的力,如重力、摩擦力、弹力、万有引力等:
void applyfriction() {PVector velocity_=velocity;velocity_.mult(-1);velocity_.normalize();PVector friction=velocity_.mult(c);applyForce(friction);}
void applygravity() {PVector gravity=new PVector(0, 0.2);applyForce(gravity);}
(本Demo中虽然有计算摩擦力的函数,但是由于一些不知名Bug以及时间有限没用实际应用摩擦力,这里只做展示)
碰撞检测
其实前人已经做了十分完善的、包含碰撞的物理库函数Box2D(该库在代码本色的第五章中有详细介绍),但是由于作业要求用到三章的知识,所以我没用调用物理库而是自己实现了一个简陋的碰撞检测机制:
Collederflag isCollision(Collider p) {float Mindis=R+p.R;if (abs(location.x-p.location.x)<=Mindis&&abs(location.y-p.location.y)<=Mindis) {if (location.x>=p.location.x) {//左侧撞击flag.horiflag=-1;} else if (location.x<p.location.x) {//右侧撞击flag.horiflag=1;}if (location.y<=p.location.y) {//下侧撞击flag.verflag=1; // println("上方");} else if (location.y>p.location.y) {//上侧撞击flag.verflag=-1;// println("下方");}return flag;} else {return flag;}}
原理大概就是根据计算每个物体的中心间的距离,根据这个距离与半径之间的关系来判断物体是否发生碰撞。在该Demo中根据两个碰撞物体的不同还有多个类似的碰撞检测函数,这里就不放了(其实只是因为本人代码能力不足,导致代码有些乱,也没用运用接口)。
粒子系统
在本Demo中存在各种不同的对象,我们将他们存储在粒子系统中。比如图片中作为边界和障碍物的砖块:
我们将他们定义为rectframe类,并在ArrayLIst frames中存储他们:
class rectframe extends frame {rectframe(PVector location_, float R_) {location=location_.get();R=R_;}void display() {rectMode(CENTER); stroke(255);strokeWeight(1);fill(0);rect(location.x, location.y, 2*R, 2*R);}
}
ArrayList<frame> frames = new ArrayList<frame>();
类似的还有图中的发射装置、发射出的子弹等,都是存储在粒子系统中的,以便于我们对一类对象进行相同的操作。
ArrayList<shootframe> shootframes = new ArrayList<shootframe>();
ArrayList<record> records = new ArrayList<record>();
ArrayList <moveblock> moveblocks=new ArrayList<moveblock>();
遍历粒子系统中的所有对象并在画面上绘制:
for (frame f : frames) {f.display();}
优化算法
为了给小球施加力,我们需要判断小球是否与其他物体发生了碰撞,但是如果每帧都让小球对所有物体进行一次碰撞检测,这显然是不现实并且十分浪费的。所以我们需要进行一些优化,首先我们对所有数据预处理。
粒子系统的预处理
我们知道粒子系统就像一个能存储结构体的数组。而将数据存入粒子系统也很简单:
List.add(f);
其中List 是ArrayList类型的对象,而f就是你要存储的数据。
但是只是存储是不够的,为了减少计算量,在每次存储新元素时,我们按照位置来插入元素,这里运用了之前在数据结构还是哪门课上学习的思路。这就好比现在给你一个乱序的数组[1,8,2,5,3,6,0,2],让你找一个大于3小于6的数,那么我们必须对每个元素进行判断,其是否大于3?其是否小于6?而如果给你一个排好序的数组[0,1,2,2,3,5,6,8],那么我们只需要从头开始判断,当我们找到数组中第一个大于3的数的位置,再从这个位置开始,找到第一个大于6的数的位置,这两个位置之间的数,就是我们需要的。但是每帧都对所有粒子系统排序显然效率也是很低的,所以我们只在生成对象,插入粒子系统的过程中,按照其x的坐标插入,就可以插入得到一个有序的粒子系统:
//---------------用于将新生成的frame对象插入ArrayList中
void insert(ArrayList List, frame f, int min, int max) {if (max==0) {//初始化List.add(f);return;}int mid=(max+min)/2;frame p=(frame)List.get(mid);//println(p.location.x);//println("mid:"+mid);//println("min:"+min);//println("max:"+max);frame pmin=(frame)List.get(min);frame pmax=(frame)List.get(max-1);//println("pmin:"+pmin.location.x);//println("pmax:"+pmax.location.x);if (f.location.x<pmin.location.x) {List.add(0, f);return;} else if (f.location.x>pmax.location.x) {List.add(max, f);return;} else if (f.location.x<=p.location.x) {frame pmid=(frame)List.get(mid-1);if (f.location.x>=pmid.location.x) {List.add(mid, f);return;}max=mid;insert(List, f, min, max);} else if (f.location.x>=p.location.x) {frame pmid=(frame)List.get(mid+1);if (f.location.x<=pmid.location.x) {List.add(mid+1, f);return;}min=mid;insert(List, f, min, max);}
}
从预处理的粒子系统中获取需要的子系统
然后当我们需要进行碰撞检测时,我们只需要根据阈值,从粒子系统中取出可能进行碰撞的子系统,对子系统中的粒子进行碰撞检测即可:
// 遍历数组,只取出ArrayList中有可能与YOU进行碰撞的对象进行碰撞检测
ArrayList<frame> findArrayList(ArrayList<frame> frames, Collider p) {ArrayList<frame> newframes=new ArrayList<frame>();float x=p.location.x;float R=p.R;float Up=x+maxR+R;float Down=x-maxR-R;//println("x:"+x);// println("Up:"+Up);// println("Down:"+Down);for (frame f : frames) {if (f.location.x>=Down&&f.location.x<=Up) {newframes.add(f);}}return newframes;
}
其他
其他还包括许多诸如开始界面的绘制、死亡提示、电影谢幕效果等,这里不放太多代码,具体直接看Demo就好:
void Gameover() {fill(0);rect(width/2, height/2, 3*width/4, height*3/4);textAlign(CENTER);fill(255);textSize(45);text("You Die", width/2, height/2);textSize(20);text("left click to continue", width/2, height/2+100);text("right click to reset", width/2, height/2+150);
}
后记
其实个人对这个Demo不是很满意,太过简陋的碰撞检测,以及还有许多想法都没有实现,对Processing和Java本身也不是很擅长,代码基础不足。以后应该会继续完善这个想法,附录放上完整的代码、可执行文件和《代码本色》PDF版。
附录
《代码本色 The Nature of Code》:链接:https://pan.baidu.com/s/1nxk5u4oIBDiEuUsOFi9Syw 密码:h0sv
《Just Die》可执行文件:链接:https://pan.baidu.com/s/1WXzLKgeGWmTb8dZENuoSBA 密码:kutl
《Just Die》源文件:链接:https://pan.baidu.com/s/1AtQ9K9eBw393o-9kyJHgoA 密码:lp6v
这篇关于交互媒体技术应用——2D横版冒险游戏初尝试的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!