壁との衝突
壁と衝突したら跳ね返ってくる処理を加えましょう。
例えば右端の壁との衝突しているかどうかは、右端の壁のx座標(width)と、粒子の円の右端のx座標の比較によって行えます。粒子のx座標は円の中心の座標なので、粒子の半径分を加える必要があります。
つまり、drawを繰り返すたびにxが大きくなっていくとき(粒子が右に向かって進んでいくとき)、x+r/2がwidthより大きくなったら
x+r/2 > width
粒子は右の壁に衝突しているというわけです。
x+r/2 == width と等号ではうまくいきません。xは小数であり、ぴったりwidthと一致することはまずありません。
衝突していたら、vxの符号をひっくり返してやります。(-1倍する)
この処理だと粒子がいくらか壁にめりこんでから衝突処理を行うことになります。次のターンでまた衝突判定が起きてしまう(1度の壁の衝突で2回衝突処理をしてしまう)ことを避けるため、粒子の位置は少し不正確になりますが、衝突位置まで粒子の位置を戻してやることにしましょう。
float elast = 1; if (x + r/2 > width) { vx = -elast*vx; x = width - r/2; }
elastは跳ね返り係数です。弾性衝突なので1でよいのですが、あとで非弾性衝突も扱えるように変数にしておきました。
上記の処理を各方向の壁について行います。前頁のプログラムに、下記の処理をclass Particleのメソッドとして追加し、draw中のparticle[i].move();の次の行に
particle[i].reflect();と入れて処理を追加します。
void reflect(){ //壁との衝突 float elast = 1; if(x + r/2 > width){ //右の壁との衝突 x = width - r/2; vx = -elast*vx; } // //ここに他の3つの壁との衝突の処理を追加してください。 // }
上の関数は右の壁との衝突だけを処理しています。
各自他の壁(左、上、下)の壁との衝突を書き加えてください。
なお、そのままだと壁に当たる前に粒子の色が薄くなって消えてしまうので、色が薄くなる処理をやめるか、薄くなる速度を遅くしてください。
無事に粒子が壁で跳ね返るようになったでしょうか。
運動方程式~重力を作用させる
次に粒子に力を作用させてみましょう。
ニュートン第2法則に従い、粒子には力に比例した加速度が生じます。
加速度a は、「単位時間あたりの速度の増加分」ですから、一定の時間間隔でコマ送りで処理している本プログラムでは、Particle内のmoveメソッドで、速度に一定値を加える処理をすればよいことになります。
ここでは重力を作用させてみることにしましょう。
分子レベルでは重力はほとんど無視できるので、あとでこの処理は消してしまうことになりますが・・・
具体的にはParticle内のmoveメソッド内の
x = x + vx;
y = y + vy;
の次の行に
vy = vy + 0.04;
と書き加えてみてください。
これでy方向下向きに一定の加速度を生じさせたことになります。
0.04は(このボックス内の)重力加速度に相当します。
単位は pixel (frame rate)2になるでしょう。pixelは画素(画面上の1ドット)、frame rate は1秒あたりに画面を書き換える回数(drawのループが1秒間に実行される回数)で、default は 10 s-1です。
まず、重力場内の運動が表現できているかどうか確かめてください。
マウスのクリック位置で粒子が発生するようにして、落とす高さを変えたときどうなるかを確認してください。
また、数値を変えて重力を変化させたり、elastの値を変えて(0~1, 例えば0.8)、非弾性衝突(だんだんと高さが低くなる)になることを確かめるなどしてみてください。