落ちる・加速する(物理演算)

2019-09-17

本章のあらすじ

  • 物理法則にもとづき自然に動かす
  • 壁や床で跳ね返らせる
速度を持つ複数エージェントに反発力/引力を加えた

速度

練習:横方向に移動を続けるエージェント

「速度」の解説の前に、簡単なスケッチを作成してみましょう。キャンバスの中央を、左から右方向に移動させてみてください。動かす量は draw() ブロック1回ごとに 5 px程度、画面の右端に達したら左端に戻します。

ヒント

  • エージェントの位置を覚えておくために変数を使う
  • 「右端に達したら左端へ」はif文で実現できる

(→vel_init

速度

速度」とは、一定時間でどのくらい位置が変わるかを表すものです。例えば「車の速度が60km/h」とは、車の位置が1時間で60km進むという状態です。

先の練習では、 draw() ブロックが1回実行されるごとに5px移動していました。 draw() 1回分を1フレームと呼ぶとき、エージェントの速度は「5px/フレーム」ですね。

時速60kmの車と、5px/フレームのエージェント

練習:速度を意識した書きかたに直してみる

先ほど作成したスケッチを、速度を意識した書きかたに直してみましょう。速度も変数として宣言して、全体を書き換えてみてください。 (→vel_hello


// エージェントの速度
float vx;
float vy;

void setup() {
  

  // 初速を設定
  vx = 5;
  vy = 0;
}

void draw() {
  // 速度を位置に反映
  x += vx;
  y += vy;

  
}

これで前と同じ動きになりました。が、わざわざ速度を変数に入れる手間をかけることで、何かよいことがあるのでしょうか?

練習:左右矢印キーで速度を変えられるようにしてみる

このままスケッチを改造して、左右の矢印キーによって速度を上げたり下げたりできるようにしてみましょう。キーを押したときに実行されるのは keyPressed() ブロックでしたね。 (→vel_key

// キーを押したときに実行されるブロック
void keyPressed() {
  // 左右矢印キーで速度を変化させる
  if (keyCode == LEFT) {
    vx -= 0.5;
  }
  else if (keyCode == RIGHT) {
    vx += 0.5;
  }
}

速度を変数に入れて覚えておくことで、速度を維持したまま少しずつ変化させることもできるようになりました。

発展

  • 左方向の移動でも画面端ループさせられるだろうか?
  • 上下方向もキーで速度を変えられるようにできるだろうか?
  • クリックに反応してランダムに速度を変えられるだろうか?

練習:跳ね返らせてみる

画面端を壁と見立てて跳ね返らせてみましょう。

「跳ね返る」とはどういう現象でしょうか?例えば右側の壁に速度 5 でぶつかった結果、方向が反転して -5 になるのが跳ね返りです。

方向が反転する

コードで書くとこんな感じ。 -1 をかけて方向を反転させるのがポイントです。

// 右端に達したら、速度を反転させる
if (x > width) {
  vx *= -1;
}

このままの他の壁の判定処理も書いてみてください。右端のif文とほとんど同じ形になりますね。

さて、現実世界において、跳ね返ったものがそのままの速度で返ってくることはまずありません。エネルギーの損失が発生して、速度は前より小さくなるのが自然です。

この現象をスケッチでどう再現すればよいかというと、 vx に掛け算している数値1-1 )をもう少し小さい数値にしてあげましょう。例えば -0.5 にすると、跳ね返り後の速度の大きさは半分になります。

(→vel_bounce

発展

  • 反発係数をいろいろ変えて動きを確かめてみよう
  • ボールが壁にめり込まないようにするにはどうすればよいだろう?

速度によるランダムウォーク

以前作ったランダムウォークでは、位置をランダムに変化させていました。 (→random_walk

このスケッチに速度の概念を取り入れて、位置ではなく速度をランダムに変化させるようにしてみましょう。



// 速度を位置に反映
x += vx;
y += vy;

// 速度をランダムに変化させる
vx += random(-0.2, +0.2);
vy += random(-0.2, +0.2);

動きの印象がだいぶ変わりましたね。速度がある程度保たれるので、ジグザグする不自然さが消えて滑らかに漂うようになりました。 (→vel_random_walk

摩擦による減速

先の例では、速度を上げるとすぐに画面外に滑っていってしまいます。(見やすいように軌跡を残してみました)

現実世界の運動では、空気抵抗(空気との摩擦)や接地面との摩擦によってエネルギーの損失が生じて少しずつ減速するのですが、これがないと速度がつきすぎてしまうのです。

この問題を解決するには、毎フレームごとに強制的に速度を小さくしていくのがもっとも簡単です。 draw() ブロック内で、速度の変数に1より小さな数を掛けてあげましょう。

// 簡易的な空気抵抗を加える
// (自然に減速=毎フレーム速度を小さくする)
vx *= 0.99;
vy *= 0.99;

適度に減速され、動きに味が出てきましたね。考えてみれば、手描きの線も物理法則のもとで手が動いて生み出されているわけです。より自然な動きに感じられるのも納得感があります。

練習:パラメーターを変えてみる

これほどシンプルなランダムウォークにも、「速度に加える変化」「減速用の数値」など、パラメーターがしっかり含まれています。これらを調整するだけでも多様な動きを生み出せます。動きのバリエーションを3つ以上作ってみましょう。

加速度

加速度」とは、一定時間でどのくらい速度が変わるかを表すものです。速度の速度、のようなものですね。例えば「車の加速度が30km/h2」とは、車の速度が1時間で30km/h大きくなるという状態です。

1時間で30km/h加速する車

練習:エージェントを加速させてみる

エージェントが右に移動するスケッチを改造して、だんだん加速するようにしてみましょう。速度 vx がだんだん大きくなるように書けばよいので…。



// 加速する(=速度を上げる)
vx += 0.05;

// 速度を位置に反映
x += vx;

こんな感じです。加速度が速度を、速度が位置を更新しているのがポイントです。 (→acc_hello

練習:重力を加えてみる

われわれがふだん感じている重力。実はこれ「重力加速度」といって、加速度の一種なのです。下方向の加速度を加えて、重力を再現してみましょう。すぐに画面外に消えてしまうので、「跳ね返り」の項を参考に床を設けてあげてください。

(→acc_gravity

発展

  • 地面に半分埋まっている問題を綺麗に直せないだろうか?

練習:クリックで弾け飛ぶようにしてみる

このままスケッチを改造して、クリックしたときにランダムな方向に弾け飛ぶようにしてみましょう。

mousePressed() ブロックで、速度にランダムな数値を加えれば実現できそうです。 (→acc_flick

void mousePressed() {
  // ランダムな方向の速度を与える
  vx += random(-10, 10);
  vy += random(-5, 5);
}

摩擦による減速再び

この例においても、エネルギーの損失がないためボールが滑っているように見えてしまいますね。

速度のところでやったのと同様に、 draw() ブロック内で速度に1より小さな数を掛けてあげましょう。 (→acc_flick_friction

だいぶ自然な動きになりましたね。

発展

  • 反重力(放っておくと上昇する)を実現できるだろうか?
  • マウスでボールを掴むようにできるだろうか? (→acc_grab
  • 勢いをつけてボールを投げるようにできるだろうか? (→acc_grab_throw
  • 速度の大きさをエージェントの表示に反映できるだろうか? (速度の大きさはmag(0, 0, vx, vy)で計算できます)
  • ボールの数を増やせるだろうか?

演習:速度・加速度を組み込んでみよう

既存のスケッチに速度・加速度を導入して、自然な動きを加えてみましょう。

配列」で作った複数体のランダムウォークに組み込めば、これまでできなかった模様を描けますし、重力を組み込めばさらに花火のような表現も可能になります。 (→vel_random_walk_array

ペンのスケッチと組み合わせれば、エージェントが勝手気ままに線を描くスケッチを作ることもできますね。

本章のまとめ

  • 速度は位置の変化、加速度は速度の変化。毎フレーム足しこむことで物理運動が再現できる
  • 重力の正体は加速度
  • 速度・加速度を組み込むことで、自然な動きが再現できる

  1. 速度にかける数値は「反発係数」「跳ね返り係数」と呼ばれ、 1 の場合はエネルギー損失のない「完全弾性衝突」になります ↩︎