【创意编程】《闪烁》:星河和弧光灯——processing粒子系统
其实前面写了很多粒子系统了,这一章已经不知道要写什么了,就没有目的地随手写了一个,不难,但是好像还挺好看的。
1.随机游走
2.鼠标划开粒子群![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/0f3898db52c7930e2dd36330c25eba1e.gif)
3.水波发散
粒子系统一直都是我非常感兴趣的一块内容,不过写多了也没有什么稀奇的了。粒子系统主要是一种辅助其他创意的工具,本身其实也就算不上什么难点,但是把粒子系统运用到更有趣的地方,充分发挥它的优势,就变成processing中可圈可点的亮点。
这一个代码也没有什么特别的想法,只是简单地实现了一个粒子系统可以实现的基本功能。我们在它没有受到外力影响的时候随机游走,在鼠标出现的位置受到人的排斥力向外扩散,距离鼠标的位置越近收到的力将更大。同时也给它添加了发光效果,即按某一个按键后从鼠标位置向外传播一道光,这圈光波的颜色随半径线性渐变并逐渐变回白色。
——————————
粒子系统构成
一般情况下,粒子系统是一个包含很多粒子的庞大的系统,每一个粒子都有各自单独的行为,相互独立不受影响。因此,我们如果想要对这些粒子进行统一的管理(在draw()函数对粒子群的刻画也属于统一管理),就需要定义一个专门用于管理粒子行为的类ParticleSystem。此外,每个粒子都是一个粒子类的实例。因此一个完整的粒子系统程序应该至少具备三个tag。
1.粒子类
每个粒子需要具备位置、速度、加速度、生命周期等成员变量,即需要有一个单独的运动对象需要具备的所有成分。一般情况下我们认为粒子都是有固定的的生命周期的,一定时间之后就会死亡。而我们需要屏幕中的粒子永远出现在画布上,需要对粒子的死亡进行处理,让它们在死亡后重生。
2.粒子管理类
一般来说管理粒子的类需要将所有粒子的统一工作比如update()、display()等方法封装起来,但它不需要知道每个粒子具体会怎样执行它们内部的更新和显示,管理类只需要知道在特定的时刻需要对所有或者部分粒子执行同样的操作,然后遍历粒子系统,对每个粒子分别执行就可。
——————————
构造粒子
一般情况下,粒子只需要用ellipse()等的processing内置的函数来表示就可以,但是我们有时候也会有一些特殊需求,除了用PShape类的beginShape()和endShape()函数来自行绘制之外,对于一些可能需要很复杂的粒子,也可以用加载外部图片来实现。processing实例中就有这样的方法。我觉得这个sprite不错,就直接拿来用了。
这里也是用的PShape来构造粒子的。
PShape part;
在粒子初始化的时候确定它的形状。由于我们会将死亡的粒子重生,所以一旦一个粒子被初始化,它的形状就不再改变。初始化函数只执行一次。
Particle() {
partSize = random(10,30);
part = createShape();
part.beginShape(QUAD);
part.noStroke();
part.texture(sprite);
part.normal(0, 0, 1);
part.vertex(-partSize/2, -partSize/2, 0, 0);
part.vertex(+partSize/2, -partSize/2, sprite.width, 0);
part.vertex(+partSize/2, +partSize/2, sprite.width, sprite.height);
part.vertex(-partSize/2, +partSize/2, 0, sprite.height);
part.endShape();
part.setTint(PartColor);
rebirth();
lifespan = random(255);
}
如上,我们再初始化的时候随机确定粒子的大小,并根据大小调整贴图构成粒子。调用一次rebirth()函数就将获取每一个粒子的类变量,创建粒子的实例,并且赋予它一定的生命周期。
————————————
随机游走
rebirth()函数中放置了初始化粒子速度的方法,这里我们直接使用random()生成-1到1 的随机数,这样可以让粒子每一帧只在八个方向中走一步,画面就会平滑很多,避免了粒子的突变。
void rebirth() {
float speed = random(0.1,0.4);
velocity = new PVector(random(-1,1),random(-1,1));
velocity.mult(speed);
lifespan = 255;
position = new PVector(random(width),random(height));
//PartColor = color(255,255,255);
part.resetMatrix();
part.translate(position.x, position.y);
}
利用这个速度,我们在update()函数中对粒子的位置进行更新,其中包括更新它的加速度、速度以及位置,并使粒子的生命周期-1。
public void update() {
lifespan = lifespan - 1;
position.add(velocity);
part.translate(velocity.x, velocity.y);
}
——————————————
鼠标排斥力的实现
这一部分也很简单。简单来说,就是每一帧获取鼠标的位置,对周围的每一个粒子计算它们和鼠标位置的距离,根据这个距离来反比求出力的大小,再运用这个力。
void detectForce(int x,int y){
for(Particle p : particles){
p.addForce(x,y);
}
}
<