Chapter 56

運動

miku
miku
2021.11.16に更新

動きの表現をより説得力のある形で書くために、この章では物理学の運動について学ぶ。

速さ

距離を時間で割ったものを速さと呼ぶ。たとえば、100mを10秒で移動したら、100m / 10秒10m/秒、つまり1秒あたりに換算した距離を計算することができる。

速度

速度は速さに向きを加えたものだ。東に移動することをプラス、西に移動することをマイナスだとして、西に100mを10秒で移動したら、-100m / 10秒 = -10m/秒、つまり1秒あたりに-10m移動していることがわかる。これはあくまで平均の速度だ。

もともとの経過時間が1秒であっても、0.1秒単位、あるいは更に小さい単位で見れば、1秒の間に色々な動きをしていて、結果1秒で出る速度も平均の速度ということになる。

let x;

function setup() {
  x = 50;
}

function draw() {
  x += 2;
}

今まで位置を変化させるにはこのようなコードを書いてきた。毎フレーム呼ばれる draw() 内で位置を表す変数に値を加えて、位置を変化させているが、このフレームを時間の単位として考えることにしよう。1フレームあたり+2pxの移動速度になるが、実はこれは平均の速度ではない。

1秒で+2mの移動というのは、0.1秒や更に小さい単位で見ることができるので結果平均の速度になるが、1フレームを更に小さい単位で分解することはできないし、draw() と次の draw() が呼ばれるフレーム間で刻々と x の値が変化しているわけではない。draw() が呼ばれるたびでないと x が変化しないので、このフレームがある種の時間の最小単位と見ることができる。このような位置に足している+2pxを瞬間の速度と呼ぶ。

つまり今まで draw() 内で位置に足していた値を速度と呼ぶことができるわけだ。今は速度を固定の値にしているが、これを変数にしてみよう。

let x, vx;

function setup() {
  x = 50;
  vx = 1;
}

function draw() {
  x += vx;
}

速度は英語でvelocityなので、略して v、あるいは軸ごとに vx, vy などの変数名になることが多い。

等速直線運動

コード全体を見る
let x, y;
let vx, vy;

function setup() {
  createCanvas(windowWidth, windowHeight);
  textAlign(CENTER, CENTER);

  reset();
}

function reset() {
  x = 0;
  y = 0;
  vx = floor(random(1, 5));
  vy = floor(random(1, 5));
}

function draw() {
  x += vx;
  y += vy;

  if (x >= width || y >= height) {
    reset();
  }

  clear();

  noStroke();
  fill(240);
  circle(x, y, 10);

  text(`vx: ${vx}, vy: ${vy}`, width / 2, height / 2);
}

軸ごとに位置 (x, y) と速度 (vx, vy) を用意して、位置に速度を毎フレーム足し合わせる。速度をこのまま変えなければ等速直線運動という動きになる。

こうなると速度を変えてみたくなるだろう。ここで出てくるのが加速度である。

加速度

加速度はその名前の通り速度に加える値のことだ。計算式としては、速度を経過時間で割った値が加速度で、速度自体に向きがあるので、計算した加速度にも向きがあることになる。たとえば車のアクセルやブレーキを思い浮かべるといい。

加速度にも平均や瞬間が存在するが、考え方は速度と同じだ。

let x, vx, ax;

function setup() {
  x = 50;
  vx = 1;
  ax = 1;
}

function draw() {
  vx += ax;
  x += vx;
}

毎フレーム速度に値を加える。これが加速度だ。速度の場合と同じようにフレームを時間の単位として考えると、瞬間の加速度と捉えることができる。

加速度は英語でaccelerationなので、略して a、あるいは軸ごとに ax, ay などの変数名になることが多い。

コード全体を見る
let x, vx, ax;

function setup() {
  createCanvas(windowWidth, windowHeight);

  x = 50;
  vx = 1;
  ax = 0.1;
}

function draw() {
  vx += ax;
  x += vx;

  clear();
  circle(x, height / 2, 10);
  
  if (x > width) {
    x = 0;
  }
}

加速度を 0.1 にして横移動した例。たしかに加速はしているが、時間が経つと速すぎてあまり現実的な動きには見えないだろう。

ここで慣性の法則というものを利用したい。

慣性の法則

慣性の法則というのは、外部から力を加えない限り、静止している物体は静止し続け、動いている物体は等速直線運動を続けるという法則だ。

力がなにかというのは後述するので置いておき、動いている物体は等速直線運動を続けるという部分に注目したい。等速直線運動は先程説明したとおり、速度が固定の運動のことだ。

要は特に外部からの影響が無ければ速度に関してはずっとその速度が維持されることになる。

function draw() {
  vx += ax;
  x += vx;
}

この考えをコードにすると、速度というのは次のフレームにそのまま引き継いでよいということだ。静止している物体は静止し続けるという記述もあるが、これは速度が 0 の等速直線運動と考えれば上と統合できる。

ただし、速度と違い、加速度は関係が無いので、今の加速度を次のフレームに引き継ぐ必要はない。

function draw() {
  vx += ax;
  x += vx;
  
  ax = 0;
}

つまり慣性の法則に従うのならば、速度に加速度を加えた後、加速度を 0 で初期化する必要がある。

function draw() {
  ax = ?; // ここで加速度を生み出さないと加速しない 
  vx += ax;
  x += vx;
  
  ax = 0;
}

これでは全く加速しなくなるので、毎フレーム速度に加速度を加える前に、加速度を生み出す何かの値を入れる必要がある。これが力である。

力と運動方程式

力というのは押したり引いたりして加速度を生み出すものだ。他にも重力、浮力、摩擦力など力が付く名前はたくさんある。

力と加速度の関係には法則があり、それを運動方程式といい、F = ma と書く。
F が力、m が質量、a が加速度である。

質量を考えなければすべて 1 にして、F = 1a つまり、F = a となり、力が直接加速度になる。

let x, vx, ax, fx;

function setup() {
  x = 50;
  vx = 1;
  ax = 0;
  fx = 1; // 力
}

function draw() {
  ax = fx;
  vx += ax;
  x += vx;
 ax = 0;
}

速度・加速度に向きがあるように、力も向きがある。力は英語でforceなので、略して f、あるいは軸ごとに fx, fy などの変数名になることが多い。

function draw() {
  ax = f0 + f1 + f2 + f3;
  vx += ax;
  x += vx;
 ax = 0;
}

力は種類がたくさんあるので、それを加速度にする場合は足し合わして加速度に代入すればいい。

function draw() {
 // 力には種類がある
  // たとえば何かを追いかけたり、風で押し戻されたりなど
  f = f0 + f1 + f2 + f3; 
  ax = f; // 力によって加速度が生まれる
  vx += ax; // 加速度によって速度が変化する
  x += vx; // 速度によって位置が変化する
  ax = 0; // 加速度は次のフレームに引き継がない
}

今までの説明をまとめるとこのようになる。

摩擦力

摩擦というのは、今加速している方向と逆向きに働く力のことである。摩擦の力は質量に依存するのだが、先程と同じように今は質量の概念がないので計算ができない。なので簡略化して、速度を落とすための係数と考えることにしよう。

ax = f;
vx += ax;
vx *= friction;
x += vx;
ax = 0;

速度に加速度を加えた後、0~1の範囲の摩擦係数掛け合わせる。もし力が無ければ、だんだんと速度が落ちていき、最終的に物体の静止するはずだ。

ベクトルでの扱い

今までx軸の移動しか扱わなかったが、y軸と合わせて運動を表現できる。

function draw() {
  vx += ax;
  vy += ay;
  x += vx;
  y += vy;
  ax = 0;
  ay = 0;
}

このように書いてもいいのだが、軸ごとに全く同じ計算をしているので、この場合はベクトルで扱うと便利になる。

let pos, v, a;

function setup() {
  pos = createVector(50, 50);
  v = createVector(0, 0);
  a = createVector(0, 0);
}

function draw() {
  v.add(a);
  pos.add(v);
  a.setMag(0);
}