2 / 4 ダイスの投擲と停止の待機
実装済みの投擲とスリープ判定を読み解き、物理シミュレーションの基本的な挙動を理解する
Rigidbodyの取得
これからスクリプトを読み解いていきます。
まずAssets/UNSLOW/DicePoker/Scripts/DiceFaceTest.csを開きます。
private void Awake()
{
_body = die.GetComponent<Rigidbody>();
}Awakeのdie.GetComponent<Rigidbody>();は、Inspectorで割り当てられたダイスからRigidbodyを取得する処理です。
次の投擲処理では、このRigidbodyに力を加えることでダイスを投げます。
撃力でダイスを投げる
SpaceキーまたはボタンでRollが呼ばれると、RollRoutineがダイスを投擲、停止を待機します。
ダイスの投擲は、物理オブジェクトを扱うRigidbodyを通じて実現します。
RollRoutineの中で呼ばれているAddForceとAddTorqueの2行を確認してください。AddForceにForceMode.Impulseを指定すると、ダイスに瞬間的な力積を与えることができます。
ダイスを投げる操作の都度呼び出すことで、ダイスを毎回異なる力で転がせます。
private IEnumerator RollRoutine()
{
// 中略
_body.AddForce(Vector3.up * 5.5f, ForceMode.Impulse);
_body.AddTorque(Random.onUnitSphere * 1.8f, ForceMode.Impulse);力積と撃力
力積とは、物体に作用する力と、その力が働いた時間を掛け合わせた物理量です。
物理学における力は物体の状態を変化させる量で、力積は力がどれだけの時間作用したかを表します。
例えば車のエンジンはアクセルを踏んでいる間、車に力を加え続けます。力が作用する時間が長いほど、車は加速します。
これが力積の例です。
一方で、バットでボールを打つように一瞬で働く大きな力を撃力と呼びます。
その作用による運動量の変化も力積で表せます。
今回はダイスを投げるためにForceMode.Impulseを指定し、瞬間的な力積である撃力を与えています。
トルク
トルクは、力が物体を回転させようとする作用を表す物理量で、「力のモーメント」とも呼ばれます。
その大きさは、力の大きさに加え、回転の中心から力を加える位置までの距離と、力を加える方向によって決まります。
物理的な物体の挙動は、力積による直線運動とトルクによる回転運動の組み合わせで決まります。
単に力を加えただけでは、物体は回転せず、直線的に動くことになります。
物体を回転させるためには同時にトルクを与える必要があります。
物体の時間当たりの移動量を速度と呼びますが、時間当たりの回転の変化量は角速度と呼ばれます。
力積の大きさを変える
Vector3.up * 5.5fはダイスを投げ上げる動きを実現する上向きの力積です。
値を大きくするほど対象の速度の変化も大きくなります。
AddTorqueは回転方向の力積を与えます。Random.onUnitSphereで回転軸をランダムに決めており、投げるたびに回り方が変わります。
- Playを止めた状態で、
AddForceの5.5fだけを6.5fへ変えて保存します。高く飛ぶか低く飛ぶかを予想してから、Playして投げます。 - Playを止め、上向きの値を
5.5fへ戻します。次にAddTorqueの1.8fだけを0.9fへ変えて保存し、Playして回転を比べます。 - 確認後はPlayを止め、回転の値も
1.8fへ戻して保存します。
回転軸は投げるたびに変わるため、同じ条件でも動きは毎回同じにはなりません。数回比べ、上向きの力積と回転の力積がそれぞれ何に関係するかを説明してください。
ダイスの停止を待って出目を読む
Rigidbodyは一定時間動きがないと自動的にスリープ状態になり、計算を休止します。
これにより、長時間動かないオブジェクトに対する無駄な物理計算を避けることができます。Rigidbody.IsSleeping()で停止状態を確認できます。
DiceFaceTest.cs内のDiceFaceTest.RollRoutine()はコルーチンで、yield returnの位置で待機します。
private IEnumerator RollRoutine()
{
// 中略
var startedAt = Time.time;
yield return new WaitForSeconds(.25f);
yield return new WaitUntil(() => _body.IsSleeping() || Time.time - startedAt >= 5f);WaitUntilは、指定された条件が成立するまで処理を待機するためのUnityのコルーチン用関数です。
ここでは、投げた直後の0.25秒と、その後ダイスが停止するか5秒経過するまで待機する処理を行っています。
最初の0.25秒は、投げた直後の物理計算がある程度進むのを待つ予備的な処理です。
その後はダイスが「スリープした」または「投げ始めてから5秒経過した」のどちらかを満たすまで待機します。
スリープした場合はダイスが安定して停止したものとみなして die.Read() を呼び、得られた出目を画面に表示します。
物理挙動のタイムアウト処理
物理オブジェクトは一定時間動きがないと自動的にスリープ状態になりますが、衝突が相互に起こり続けてスリープできない場合もあります。
この挙動を完全に回避するのは困難です。
ここではダイスが5秒経過してもスリープしなかった場合、タイムアウトとして再度ダイスを投げられる状態に戻しています。
この時点でのDiceFace.Read()は未実装で、単に0を返す関数となっているため画面には「結果:0」と表示されます。
次のステップで出目の読み取りを実装します。
- 力積とトルクの関係を理解した
- スリープによる停止確認の役割を理解した