[Unity] Rigidbodyのスリープ問題

2ヶ月くらい前のゲームジャムで嵌っていた問題のメモ書きです。

玉転がしゲームで以下のように水平面に球体を落として平面を回転させたとき、ボールがその場で止まってしまいます。

rbbody-sleep

分かり易くするため、ボールがスリープ状態のときは赤色に、そうでないときは緑色に表示しています。
ボールが平面に完全に静止したあとにスリープし、その後に平面を傾けてもスリープ状態のまま動かなくなっていることが確認できます。

平面をほんのわずかでも傾けておくと上記の問題は発生しなくなります。

Unityのリファレンスマニュアルによると、Rigidbody自体がスリープ状態に入ってしまうと以下の条件を満たさない限りウェイクアップしないためです。

  ・別の Rigidbody がスリープしているRigidbodyに衝突した場合
  ・ジョイントを通じて接続された別のRigidbodyが移動し始めた場合
  ・Rigidbody のプロパティ編集時
  ・Forceの追加時

上記の例では、平面を傾けてもボールに衝突したりしないため結果として空中停止していました。
このような現象を解決するためには、平面を動かすとき等、ボールが動いてほしいタイミングでスクリプトからウェイクアップする必要があります。

以下のようなスクリプトを追加してキー入力で明示的にボールのRigidbodyをウェイクアップさせてみました。

	// Update is called once per frame
	void Update () {
		var body = GameObject.Find ("Sphere").GetComponent<Rigidbody> ();
		if ( Input.GetKey(KeyCode.A) ) {
			body.WakeUp();
		}
	}

rbbody-sleep2

ゲーム画面にフォーカスが当たった状態で「A」キーを押すと強制的にウェイクアップが掛かり、静止していたボールが動き出すようになりました。

物体がウェイクアップして動き出す動作は通常は意識することが無いですが、もし予期せずにその場でとまってしまうことがあったら上記を疑ってみると良いかもしれませんね。

■参考サイト
Rigidbody のスリープ / Rigidbody Sleeping