Processingで物理 等速度運動と落下運動

Processingで気持ちの良い動きを実現する方法です。気持ちの良い動きとは私たちが日常の中で見かける自然な動きです。その動きを画面の中で再現するには物理的なアプローチが有効です。

まずは基本的なところから入っていきます。必要になるのは高校理科や数学の範囲です。

運動方程式

運動方程式は次のような式になります。

F = m \times a

Fは力
mは質量
aは加速度
質量と加速度を掛けたものが力になります。質量は重さ。加速度は速度の変化です。変化の時間をdTとすると次のように書き表せます。

F=m \times \frac{v_2 - v1}{dT}

v2は変化後の速度、v1は変化前の速度
dTは変化にかかった時間になります。一般の物理の場合にはlim dT→0として微分を使ったりします。今回はProcessingでの表示が目的なのでdTが1フレームとなります。dT=1とします。こんかいはさらに簡単にするために質量mも1とします。式は次のようになります。

F=v_2 - v_1

v2は変化後の速度、v1は変化前の速度。
v2を左辺に移動して式を整理します。

v_2=F - v_1

つまり「速度v1で移動している物体に力Fが加わると速度がv2に変化する。」
という事になります。

等速度運動

さてここからProcessingのコードに入ります。物体が一定の速度で移動する等速度運動を作ってみます。画面中央に円を1つ描きます。これが物体という事になります。

float v, F, x, y;

void setup() {
  size(800, 200);
  x = 100;
  y = 100;
  v = 0;
  F = 0;
}

void draw() {
  background(0);
  
  ellipse(x, y, 20, 20);
}

上記のプログラムは円(物体)を表示するだけです。xとyは物体を描画する座標。vは速度です。先ほどの運動方程式ではv2とv1の2つがありましたが、mouseClicked()の中で力Fを加えてvを更新します。
上記のプログラムにクリックイベントを追加します。

void mouseClicked() {
  F = 1;
  v = F+v;
}

マウスをクリックすると速度vにFが加わります。そしてdrawループの中で位置の更新を行います。プログラム全文は次の通りです。

float v, F, x, y;

void setup() {
  size(800, 200);
  x = 100;
  y = 100;
  v = 0;
  F = 0;
}

void draw() {
  background(0);
  
  x = x + v;

  ellipse(x, y, 20, 20);
}

void mouseClicked() {
  F = 1;
  v = F+v;
}

クリックすると円が右方向に移動します。
これで等速度運動が実現できました。現実世界に例えると水平な台に置いた物体に瞬間的に力を加えて動かしたのと同じです。指でおはじきを弾いたのと同じようなものです。そして抵抗が無いのでいつまでも動き続けます。

減衰運動

抵抗が無く、いつまでも動くのでは不自然に見えます。そこで徐々に速度が減っていくようにしてみます。いくつかやり方がありますが、簡単なのはdraw()の中で速度に1.0以下の数値をかけて減らしていく方法です。

void draw() {
  background(0);
  v = v * 0.98;
  x = x + v;

  ellipse(x, y, 20, 20);
}

上のコードでは0.98を掛けているのでdrawを実行するたびに2%づつ速度が減っていく事になります。

跳ね返り

現在のコードでは移動していくと画面から消えて見えなくなります。これでは面白く無いので端までいったら壁で跳ね返るようにしてみたいと思います。壁にぶつかる、つまり座標が画面幅以上になったら速度を反転(マイナスをつける)します。

void draw() {
  background(0);
  
  x = x + v;
  
  if (x > 800){
    x = 800;
    v = -v;
  }

  ellipse(x, y, 20, 20);
}

x座標が800以上になった場合に座標を800にします。そして速度を反転します。これで右側の壁で跳ね返ります。
次に左側で跳ね返るようにします。左は0以下になったら跳ね返るようにします。

void draw() {
  background(0);
  
  x = x + v;
  
  if (x > 800){
    x = 800;
    v = -v;
  }
  
  if (x < 0){
    x = 0;
    v = -v;
  }

  ellipse(x, y, 20, 20);
}

等加速度運動

次に一定の加速度で速度が変化する等加速度運動を作ってみます。絶えず力を受けて運動しています。一番身近な例では自由落下です。落下中は地球の引力を絶えず受けて加速していきます。式は次のようになります。

v_2=g + v_1

ここでgは重力の力になりますがコードの中ではFxと表記しています。力の方向をxとy方向にわけ、それぞれFx, Fyとしています。速度も同様にvxとvyとしています。コードは次のようになります。

float vx, vy, x, y;
float Fx,Fy;

void setup() {
  size(800, 800);
  x = 100;
  y = 100;
  vx = 0;
  vy = 0;
  Fx = 0;
  Fy = 0.2;
}

void draw() {
  background(0);
  
  vy = Fy+vy;
  
  x = x + vx;
  y = y + vy;
  
  if (x > 800){
    x = 800;
    vx = -vx;
  }
  
  if (x < 0){
    x = 0;
    vx = -vx;
  }

  ellipse(x, y, 20, 20);
}

void mouseClicked() {
  Fx=1;
  vx = Fx+vx;
}

Fy=0.2としています。この部分を変えると落下の速度が変わります。現実の地球上では重力加速度は9.8m/s^2と決まっていますが、画面の中では適当に決められます。画面の中では特に単位を決めていないので適当に見た感じで決めましょう。

等速度運動の場合はマウスクリックで一瞬の力を加えていましたが、等加速度運動(落下)では常に力が加わり続けるのでdrawループの中で

vy = Fy+vy;
  
x = x + vx;
y = y + vy;

として速度を常に増加するようにしています。
y方向(下方向)の力は絶えず加わり、速度が増え続けます。x方向(横方向)の速度はここでは加わらないので一定になります。

床で跳ね返る

先ほどのサンプルでは画面の下まで永遠に落ち続けます。それでは面白くないので画面の下(地面)で跳ね返るようにしてみましょう。先ほどの横方向での跳ね返りと同じようなものです。

if (y>800){
    y = 800;
    vy = -vy;
  }

画面の高さが800pxなので800で跳ね返るようにしています。ellipseで20pxの円を描いているのでその半径分を引いても良いでしょう。そしてこのままでは永遠に同じ高さまで跳ね上がり違和感があります。現実世界では空気抵抗や跳ね返りの抵抗があります。ここでは跳ね返りの際に少し速度が遅くなるようにしてみます。先ほどの等速度運動に抵抗をいれたのと同じように跳ね返りで

if (y>800){
    y = 800;
    vy = -vy*0.9;
    vx = vx*0.9;
  }

とするだけです。これで床にぶつかった時に最初よりも低い高さまでしか跳ね上がりません。x方向にも減速を入れているので、しばらくすると停止します。コード全文は次の通りです。

float vx, vy, x, y;
float Fx,Fy;

void setup() {
  size(800, 800);
  x = 100;
  y = 100;
  vx = 0;
  vy = 0;
  Fx = 0;
  Fy = 0.2;
}

void draw() {
  background(0);
  
  vy = Fy+vy;
  
  x = x + vx;
  y = y + vy;
  
  if (x > 800){
    x = 800;
    vx = -vx;
  }
  
  if (x < 0){
    x = 0;
    vx = -vx;
  }
  
  if (y>800){
    y = 800;
    vy = -vy*0.9;
    vx = vx*0.9;
    
  }

  ellipse(x, y, 20, 20);
}

void mouseClicked() {
  Fx=1;
  vx = Fx+vx;
}