この記事はProcessing Advent Calendar 2021の24日目の記事です。
はじめに
blenderで作成している雪だるまくんに最適な雪を見つけたくて作成しました。
実装したソースコードはいかにあります。
GitHub
データの管理
データはJavaのCollectionであるArrayList
を使い管理しています。
ArrayList
は可変長配列を扱うことが可能なデータ構造で動的に値を追加したり削除が可能なため使おうと思いました。
SnowParticle
早速SnowParticleを制作します。
SnowParticleは大きく分けて2つのクラスによって実装されています。
SnowParticle
SnowParticle自体に関わるクラスでParticleの位置や速度、加わっている力、質量、マテリアルなどの要素を定義しています。そのため1つ1つのParticleに速度や大きさなどに異なるパラメーターを割り当てが行えます。
コンストラクター
SnowParticleのコンストラクターを見ていきます。
SnowParticle(PVector initPosition, float initMass, float particleMax){
position = initPosition.copy();
velocity = new PVector(0.0, 0.0, 0.0);
acceleration = new PVector(0.0, 0.0, 0.0);
mass = initMass;
particleCounter = particleMax;
}
particleCounter
変数は生成Particleの最大個数を動的に変化させることができます。大きければ大きいほどParticleの生成個数は増えます(その分重くなります)。
位置ベクトルがcopy
で呼び出されているのは名前空間汚染を避けるためです。
addForce
力を加えるメソッドで加速度ベクトルを求めます。
力は重力や風力などで加え方によってParticleの動きを大きく変化させます。
void addForce(PVector force){
PVector calcForce = PVector.div(force, mass);
acceleration.add(calcForce);
}
update
値の更新を行います。
particleCounter
は1ずつ減らしていきParticleの削除を行うかどうかの判定を行うための準備しています。
void update(){
// 速度ベクトルに加速度ベクトルを加算
velocity.add(acceleration);
// 位置ベクトルに速度ベクトルを加算
position.add(velocity);
// 加速度ベクトルを0にリセットする。
acceleration.mult(0.0);
// Particleの個数をカウントする。
particleCounter -= 1.0;
}
render
Particle自体の描画を行うためのメソッドです。
ここでParticleの大きさや色を決定します。
void render(){
float size = mass;
// stroke(200, 200, 200);
fill(240, 240, 250);
pushMatrix();
translate(position.x, position.y, position.z);
ellipse(0, 0, size, size);
popMatrix();
}
isCheckEdge
このメソッドはできる限りParticleが画面外に出ないようするためです。
x軸のみ定義されています。できる限り画面表示したいので特定の領域に達すると跳ね返り反対方向に飛んでいきます。
void isCheckEdge(){
float positionMaxX = 100;
if(position.x < 0.0){
position.x = 0.0;
velocity.x *= -1.0;
} else if(position.x > width) {
position.x = width + positionMaxX;
velocity.x *= -1.0;
}
}
isRemove
Particleの削除を行うかどうかの結果のみ返します。
削除自体はもう1つのクラスであるSnowParticleSystem
で制御します。
boolean isRemove(){
if(particleCounter < 0.0){
return true;
} else {
return false;
}
}
SnowParticleSystem
SnowParticle
クラス自体を追加や配列からの削除、メソッドの呼び出しなど重要な制御をします。
コンストラクター
SnowParticle
クラスをArrayList
で定義しています。
wind
は風力の定義で今後外部からコントロールできるようコンストラクターで初期化をしています。
ArrayList<SnowParticle> particles;
PVector wind;
boolean flag;
SnowParticleSystem(){
particles = new ArrayList<SnowParticle>();
wind = new PVector(0.01, 0.0);
// 今は気にしなくて良いです。
flag = false;
}
addParticleSystem
ArrayList
にSnowParticle
クラスを追加します。この際Particleの位置ベクトルや質量などの値も生成しています。
void addParticleSystem(){
// particleMaxは適切でないので移動する予定です
float particleMax = 500;
PVector position = new PVector(random(-width * 0.5, width), -height * 0.1);
float mass = random(1.0, 5.0);
particles.add(new SnowParticle(position, mass, particleMax));
}
updateParticleSystem
ArrayList
にあるSnowParticle
クラス自体と定義メソッドの制御をしています。
void updateParticleSystem(){
for(int count = particles.size() - 1; count >= 0; count--){
SnowParticle particle = particles.get(count);
float mass = particle.mass;
// 重力の定義 Particleの質量をかける。
PVector gravity = new PVector(0.0, 0.01 * mass);
// 風力を追加
particle.addForce(wind);
// 重力を追加
particle.addForce(gravity);
// 値の更新
particle.update();
// Particleの描画
particle.render();
// Particleが画面外かどうかの判定
particle.isCheckEdge();
// 削除するかどうかの判定
// trueならば削除
if(particle.isRemove()){
// ArrayListのremoveメソッド
particles.remove(count);
}
}
}
つまずいたところ
2Dで実装したあと3D実装する予定でしたがカクカクしてしまい、問題を解消中です。
今後の予定
- コンストラクターで制御できるパラメーターを増やす。
- blenderやGLSLと組み合わせる。
- 吹雪感を出す。
- データ構造やアルゴリズムの深い知識
- 3Dで実装すると同時にフレームレートが落ちてしまう問題を解消する。
参考文献
- The Nature of Code: Simulating Natural Systems with Processing
- プログラミングコンテスト攻略のためのアルゴリズムとデータ構造
- 世界で闘うプログラミング力を鍛える本~コーディング面接189問とその解法~