刻みが粗いと壊れる
毎フレーム位置を += 速度 で一歩進める。この一歩の幅が刻み(タイムステップ)。幅が狭いうちは、なめらかな曲線を細かい折れ線で近似しているだけで、軌道はほぼ正しい。幅を広げると折れ線が曲線から外れ、外れた位置で計算した力がさらに外れを呼ぶ。ばねや重力のように位置で力が変わる系では、その誤差が雪だるま式に膨らんで、振幅がどんどん増えて発散する。
オイラー積分(v += a; x += v)は一歩ごとに本物の軌道から外側へずれ、そのぶんエネルギーをわずかに足してしまう。減衰のないばねでも振幅が育って発散するのはこのため。同じ二行でも順序を入れ替えた x += v; v += a はシンプレクティック・オイラーで、エネルギーが一定値の周りで振動するだけで増え続けない。Verlet がオイラーより安定なのも、この誤差の溜まり方がおとなしいから。刻みを固定して、経過時間を貯め、貯まったぶんだけ固定刻みで回すアキュムレータを使うと、表示の滑らかさと計算の正しさが切り離せて、同じ初期条件なら毎回同じ結果になる(決定性)。
ばねの一歩はこの二行。中心からのずれ x に比例した力で速度を引き戻し、その速度で位置を進める。k がばねの硬さ。
// ばね 1 ステップ。x は中心からのずれ v += -k * x // ずれに比例した力で速度を引き戻す x += v // その速度で位置を進める
これを一フレームに一回だけ回すと刻みが粗くなる。k を強めにしたばねを一度はじいて、振れ幅の履歴をそのまま横へ流す。
弾いた直後は正弦の形で揺れるのに、波が進むほど振れ幅が育って、画面の上下へ振り切れていく。220 フレームごとに弾き直さないと、すぐ振り切れて消える。式は本物のばねと同じなのに、振幅だけが時間とともに増えていく。
直し方は、一フレームをさらに細かく刻むこと。同じ二行を sub 回に分けて、一回ぶんの dt を 1 / sub にする。刻みの幅だけ変えた三本を並べる。
三本とも同じばね、同じ初期条件なのに、上の sub 1 は揺れるたびに振幅が育って振り切れる。真ん中の sub 2 でだいぶ落ち着き、下の sub 8 はきれいな正弦のまま揺れ続ける。式は一文字も変えていない。一フレームの中で何回刻むかを増やしただけで、壊れていた式が安定する。sub を上げるほど計算は重くなるかわりに、軌道が本物のばねへ近づく。
検出して、めり込みを直す。その直し方はどれも刻みという同じ床の上に乗っていて、床が粗いと上物がいくら正しくても崩れる。ぶつかった瞬間に運動量を正しく交換するインパルス応答、速い物体がすり抜けないようにする連続衝突判定、回転を持つ剛体は未踏。