一、 粒子系统
从20世纪80年代初开始,粒子系统就被用于制作各种电子游戏、动画、数码艺术作品,还被用于模拟各种不规则的自然现象,比如火焰、烟雾、瀑布、草丛和泡沫。
这一章讨论粒子系统的实现策略。我们将探讨一下问题:在实现粒子系统时,如何组织代码;如何存放单个粒子及整个系统的相关信息。在模拟过程中,用最基本的图形代表例子,并且只涉及粒子的最基本行为(比如在重力作用下的行为)。
在粒子系统的研究过程中,我们将会接触两种面向对象编程的关键技术:继承和多态。在前面的例子中,数组中存储的只是同种类型的对象,就像Mover和Oscillator。有了继承和多态之后,我们就可以在数组中存放不同类型的的对象。
首先尝试模拟单个粒子的运动情况,粒子发射出来后受重力逐渐坠落并消亡。
二、 例子一
1、在Particle类中定义所需变量位置、速度、加速度以及生命周期。在particle构造函数中给变量赋值。
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;
}
}
2、update()函数更新粒子每一帧的速度、当前位置以及生命长度。Display()函数中画出粒子。IsDead()判断粒子的生命是否结束,如果结束、不在绘制此点。
void update() {
velocity.add(acceleration);
position.add(velocity);
lifespan -= 2.0;
}
void display() {
stroke(0, lifespan);
strokeWeight(2);
fill(127, lifespan);
ellipse(position.x, position.y, 12, 12);
}
boolean isDead() {
if (lifespan < 0.0) {
return true;
}
else {
return false;
}
}
3、 主函数中new一个对象并调用。Draw()函数中,如果isDead()返回值为true,则新发射一个粒子。显示运行结果
Particle p;
void setup() {
size(500,500);
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));
}
}
三、 由系统组成的系统
如果系统中的粒子数量位置,该如何管理粒子列表呢。
我们可以使用数组管理。如果粒子系统的粒子数量是恒定的,数组是非常有效的工具。此外,processing还提供了一些函数用于改变数组长度,比如expend()、contract()、subset()、splice()等。这里,我使用ArrayList类,这是一种更高级的对象列表管理方法。
ArrayList的实现思路与普通数组类似,但语法并不相同。
用数组实现:
int total=10;
Particle[] parray = new Particle[total];
void setup(){
for(int i=0;i<parray.length;i++){
parray[i]=new Particle();
}
}
void draw(){
for(int i=0;i<parray.length;i++){
Particle p=parray[i];
p.run();
}
}
}
用ArrayList实现:
int total=10;
ArrayList<Particle>plist=new ArrayList<Particle>();
void setup(){
for(int i=0;i<total;i++){
plist.add(new Particle());
}
}
void draw(){
for(int i=0;i<plist.size();i++){
Particle p=plist.get[i];
p.run();
}
}
在两种实现路径中,最后一个for循环的实现方式相似,都是通过一个下标遍历数组中的每个元素,我们创建了变量i,让他从0开始每次递增1,诸葛访问ArrayList的元素,知道遍历结束。除此之外,Java还提供一种更简洁的改进型for循环。
ArrayList<Particle>plist=new ArrayList<Particle>();
for(Particle p:particle){
p.run();
}
ArrayList类提供remove()函数,可以通过这个函数移除某个粒子对象(通过对象的下标)。在判断粒子对象的isDead()函数返回值是否为true后,如果是,则调用remove()移除对象。
粒子系统的是由一系列独立对象组成的集合,我们可以让这些粒子系统对象组成一个集合,产生一个由系统组成的系统。
四、 例子二。
1、在Particle类中定义所需变量位置、速度、加速度以及生命长度。在构造函数中给变量赋值。
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(-2,0));
position = l.get();
lifespan = 255.0;
}
}
2、update()函数中更新粒子的速度、加速度以及缩短生命长度。Display()函数中画出初始粒子的位置。
void update() {
velocity.add(acceleration);
position.add(velocity);
lifespan -= 2.0;
}
void display() {
stroke(0,lifespan);
fill(127,lifespan);
ellipse(position.x,position.y,12,12);
}
3、ParticleSystem粒子系统类。创建ArrayList数组particles。在构造函数中给入参数粒子数量num和起始位置v。添加num数量的粒子进入arraylist数组。
class ParticleSystem {
ArrayList<Particle> particles;
PVector origin;
ParticleSystem(int num, PVector v) {
particles = new ArrayList<Particle>();
origin = v.get();
for (int i = 0; i < num; i++) {
particles.add(new Particle(origin));
}
}
}
4、run()函数中遍历arraylist数组创建粒子,如果isDead()函数返回真值,则remove()清除该粒子。
void run() {
for (int i = particles.size()-1; i >= 0; i--) {
Particle p = particles.get(i);
p.run();
if (p.isDead()) {
particles.remove(i);
}
}
}
5、主函数中new一个系统对象(注意是ArrayList)并调用,利用上边提到的简易for循环不断执行操作。
ArrayList<ParticleSystem> systems;
void setup() {
size(500,500);
systems = new ArrayList<ParticleSystem>();
}
void draw() {
background(255);
for (ParticleSystem ps: systems) {
ps.run();
ps.addParticle();
}
}
6、添加一个鼠标获取起始点的函数,在鼠标点击时,获取mouseX和mouseY传给ParticleSystem。在点击点开始运行整个系统。
void mousePressed() {
systems.add(new ParticleSystem(1,new PVector(mouseX,mouseY)));
}
五、 总结
本章中掌握了粒子系统的实现,并且会运用ArrayList数组对单个粒子对象管理,从而实现系统的粒子系统。在随后的实践操作中,可以添加前边学到的各种力的作用,实现受力作用的粒子系统、带排斥对象的粒子系统。以及进行粒子纹理化渲染图像。