鲤——processing动画交互应用
前言
鲤鱼在我们的日常生活中是一种非常常见的鱼类,它们中的一部分会成为餐桌上的食物,另一部分——锦鲤,则具有极高的观赏价值。我们在园林、寺庙等景点的水池中经常能看见色彩艳丽的各类锦鲤,其中红色和金色的锦鲤最受欢迎。
我从很久以前就很喜欢锦鲤的形象,不过比起现实中的锦鲤,我更喜欢的是中国风的水墨画或水彩中的锦鲤,无论是细细描绘还是草草勾勒,鱼身的弧度和色泽都显得灵动缥缈,仿佛下一刻这尾鱼儿就会从画中跳出来,一摆尾便消失在视野中。
说到鲤,说不定有人会想起一款叫《鲤》的游戏,我当初是在taptap上下载到这个游戏的,画面风格和游戏方式都很清新休闲,不过现在好像无法下载了(不知道发生了啥)。这次作业主题也是因为回想起了这部游戏才产生的,想要实现一个可以控制鲤来完成一些任务的小游戏。
目录
应用设计
自己最初的想法虽然好,但在编写过程中遇到的问题太多了,方案也就变得越来越简单,这里就只写最后成型的程序内容吧。
1.首先是最基本的,鼠标移动控制鲤鱼在池塘中自由游动,并伴有一定的动画效果。
2.鼠标按下时,会给鱼一个当前方向的力,让鱼儿加速。
3.鲤鱼游动时会不断吐出泡泡,泡泡受到浮力与重力影响(浮力肯定是要大一点的)向上移动。
4.随机地在池塘中产生鱼食,产生时会有波纹,鱼食经过一段时间后消失,鲤鱼吃到鱼食时会在当前位置动态出现莲花。
5.鲤鱼最初为白色,每吃一次鱼食就会加深颜色直到最终变红。
6.画面上方有文字提醒,当鲤鱼变成最终心态后赋予一个固定的力让鱼游动,游戏结束。
编写过程将按照设计内容来一一解说。
编写过程
1.虽然我也很想自己设计一个完整的鲤鱼动画…不过时间紧迫加上能力限制,最终使用的鲤鱼蓝本来源于一个开源项目(看到这个项目也促使了我做这个主题),我借鉴了其中绘制鲤鱼的一部分代码,是哪个网站找到的我记不得了…
项目运行页面:http://mylifeaquatic.herokuapp.com/
github源码:https://github.com/dasl-/my-life-aquatic
项目源码好像是需要用ruby运行的(不太了解),所以我只看了proccessing文件夹里面的代码进行参考(在public/processing里面),最终借鉴的是三个文件中的部分代码:Boid,Fish和Flagellum。
Boid类(包括我自己的注释):
class Boid {
public PVector location;
public PVector velocity;
private PVector acceleration;
private float maxForce;
private float maxSpeed;
public Boid( PVector _location, float _maxSpeed, float _maxForce) {
createBoid(_location, _maxSpeed, _maxForce);
velocity= new PVector( random( -maxSpeed, maxSpeed ), random( -maxSpeed, maxSpeed ) );
}
private void createBoid(PVector _location, float _maxSpeed, float _maxForce) {
location = _location;
maxSpeed = _maxSpeed;
maxForce = _maxForce;
acceleration = new PVector( 0, 0 );
}
protected void update() {
velocity.add(acceleration);
velocity.limit(maxSpeed*2);
location.add(velocity);
acceleration.mult(0);
}
private PVector steer( PVector _target, boolean _slowdown ) {
PVector steer;
PVector desired = PVector.sub( _target, location );//target minus location now
float dist = desired.mag();// get vector length
if ( dist > 0 ) {
desired.normalize();//danweihua vector,make the length=1
if ( _slowdown && dist < 60 ) {
desired.mult( maxSpeed * (dist / 60) );
}
else {
desired.mult( maxSpeed );//direction*speed
}
steer = PVector.sub( desired, velocity );
steer.limit( maxForce );
}
else {
steer =new PVector( 0, 0 );
}
return steer;
}
protected void seek(PVector _target) {
acceleration.add( steer( _target, false ) );
}
}
Boid主要掌管鲤鱼的运动,其中的update和代码本色中的Mover类很相似,由位置,速度,加速度和力的层层累加完成当前时刻鲤鱼位置的计算,不同的是鲤鱼的速度有最大上限,此处最大上限为定义的最大速度(maxspeed)的两倍,原因在下面再解释。这个类被创建时,会定义最初位置,最大速度与最大力并给予一个随机的初始速度。
下面的seek与steer函数则是控制鱼类向鼠标移动的关键,steer函数接收目标位置target(slowdown参数没用到不用管),用target减去当前位置location得到运动需要的向量desired,单位化这个向量,之后将它乘以初始最大速度,就得到了所需速度(包括方向),用所需速度减去目前速度,得到所需加速度,因为此处不计重量因此用最大力来约束这个加速度后,就可以得到最终的所需加速度。使用seek函数接收目标即可更新当前加速度,成功完成导向。seek函数与代码本色中的自制智能体有关。
Fish类继承了Boid类,而Flagellum类主要用于确定每一时刻的鲤鱼各个部分的位置与形状,在Fish类中定义了五个Flagellum类:身体,两半尾巴和两个鱼鳍,Fish类中的render函数负责绘制整个鲤鱼,这些部分我粗略看了之后没怎看懂,后来也没有更多时间 细细看了,有兴趣的朋友可以去下了源码研究研究。Fish的update函数中这一部分承接上文实现鲤鱼的游动: