书名:代码本色:用编程模拟自然系统
作者:Daniel Shiffman
译者:周晗彬
ISBN:978-7-115-36947-5
目录
4.2 单个粒子
1、单个粒子类
- 粒子就是在屏幕中移动的对象,
- 它有位置、速度和加速度变量,
- 有构造函数用于内部变量的初始化,
- 有display()函数用于绘制自身,
- 还有update()函数用于更新位置。
class Particle {
PVector location; Particle对象是Mover对象的别名,它有位置、速度和加速度
PVector velocity;
PVector acceleration;
Particle(PVector l) {
location = l.get();
acceleration = new PVector();
velocity = new PVector();
}
void update() {
velocity.add(acceleration);
location.add(velocity);
}
void display() {
stroke(0);
fill(175);
ellipse(location.x,location.y,8,8);
}
}
2、生存期
这是一个很简单的粒子,我们可以继续完善这个粒子类:
- 可以在类中加入applyForce()函数用于影响粒子的行为(后面的例子会实现这一特性);
- 可以加入其他变量用于描述粒子的色彩和形状,或是用PImage对象绘制粒子。
- 但现在,我们只想在类中加入一个额外的变量:生存期(lifespan)。
典型的粒子系统中都有一个发射器,发射器是粒子的源头,它控制粒子的初始属性,包括位置、速度等。
- 发射器发射的粒子可能是一股粒子,也可能是连续的粒子流,或是同时包含这两种发射方式。
- 有一点非常关键:在一个典型的粒子系统中,粒子在发射器中诞生,但并不会永远存在。
- 假设粒子永不消亡,系统中的粒子将越积越多,Sketch的运行速度也会越来越慢,最后程序会挂起。
- 新的粒子不断产生,与此同时,旧的粒子应该不断消亡,只有这样,程序的性能才不会受到影响。
- 决定粒子何时消亡的方法很多,
比如,粒子可以和另一个粒子结合在一起,或在离开屏幕时消亡。
这是本章的第一个粒子类(Particle),我希望它尽可能简单,因此用一个lifespan变量代表粒子的生存期,这个变量从255开始,逐步递减,递减到0时粒子消亡。
加入生存期后的粒子类如下所示:
class Particle {
PVector location;
PVector velocity;
PVector acceleration;
float lifespan; 该变量用于管理粒子的“生存”时长
Particle(PVector l) {
location = l.get();
acceleration = new PVector();
velocity = new PVector();
lifespan = 255; 为了便于实现,我们从255开始递减
}
void update() {
velocity.add(acceleration);
location.add(velocity);
lifespan -= 2.0; 递减生存期变量
}
void display() {
stroke(0, lifespan); 由于生存期的范围是255~0,我们可以把它当成alpha值
fill(175,lifespan);
ellipse(location.x,location.y,8,8);
}
}
为了便利,我们让生存期从255开始递减,直至减到0。因为我们让粒子的alpha透明度等于生存期,所以当粒子“消亡”时,会变得完全透明,这样它在屏幕中也就不可见了。
3、检查粒子函数
- 有了lifespan变量之后,我们还需要添加另一个函数,
它返回一个布尔值,用于检查粒子是否已经消亡。 - 这个函数将在粒子系统类的实现中派上大用场。
粒子系统的主要职责是管理粒子的列表。 - 这个函数的实现非常简单,
我们只需要检查lifespan变量是否小于0:如果小于0,就返回true;如果不小于0,就返回false。
4、示例
在创建多个粒子对象之前,应该确保粒子类能够正常工作,我们可以用Sketch模拟单个粒子对象的运动情况。
模拟代码如下所示,在其中加入了两个额外功能:
- 首先,为了方便调用,在其中加入了run()函数,
这个函数只是简单地调用了update()函数和display()函数; - 其次,为了模拟重力,
我们为粒子对象赋予一个随机的初始速度和向下的加速度。
示例代码4-1 单个粒子
Particle p;
void setup() {
size(640,360);
p = new Particle(new PVector(width/2,20));
background(255);
smooth();
}
void draw() {
background(255);
p.run();
if (p.isDead()) {
p = new Particle(new PVector(width/2,20));
//println("Particle dead!");
}
}
Particle.pde
class Particle {
PVector position;
PVector velocity;
PVector acceleration;
float lifespan;
Particle(PVector l) {
acceleration = new PVector(0, 0.05);
velocity = new PVector(random(-1, 1), random(-1, 0));
position = l.get();
lifespan = 255.0;
}
void run() {
update();
display();
}
// Method to update position
void update() {
velocity.add(acceleration);
position.add(velocity);
lifespan -= 2.0;
}
// Method to display
void display() {
stroke(0, lifespan);
strokeWeight(2);
fill(127, lifespan);
ellipse(position.x, position.y, 12, 12);
}
// Is the particle still useful?
boolean isDead() {
if (lifespan < 0.0) {
return true;
}
else {
return false;
}
}
}