Unity Physics.Overlap関数で衝突判定をスマートに
Unityにて、オブジェクトの当たり判定を実装するとき、コライダーとOnCollisionEnterイベントを使うだろうか。ゲームオブジェクトに、Boxコライダーを付けて、OnCollisionEnterやOnCollisionStayコールバックイベントで、検知するといった具合だ。
細かく見ていくと、この衝突イベントは、Unityの処理ライフサイクルのPhysics物理演算のループで、FixedUpdate()の最後に呼ばれる。私たちがPhysics関連の処理を書くFixedUpdate()があって、Unity内部のPhysics物理演算がされて物理状態が更新されて、その「後に」、OnTriggerXXX()とOnCollision()が呼ばれる。順番を気にされたい。
Update()の方は、ユーザー入力からの何かの処理や、ゲームロジックを書くが、FixedUpdate()とは、全く別のループで動いていると捉えて良い。Update()の間に、FixedUpdate()がある場合もあるし、ない場合もある。Fixed Timestepの設定値とUpdate()の処理時間の関係でそうなる。
ここで一つ注意すべき点が出てくる。Update()関数のゲームロジックから見ると、まだ、衝突判定がされておらず、次の周のループのFixedUpdate()の最後まで、その判定を待たないといけないケースだ。例えば、ゲームオブジェクトを動的に生成して、動的にコライダーを付けて、衝突しているか知りたいときだ。その時点のUpdate()関数内では、判定結果が即座に得られないのだ。
Physics物理演算のループ※いわゆるFixedUpdateループ
1) FixedUpdate()2) Unity内部のPhysics物理演算
3) OnTriggerXXX()
4) OnCollisionXXX()
ゲームロジックのループ ※いわゆるUpdateループ
1) ユーザー入力取得2) Update() <-- ゲームオブジェクトを動的に生成して、ここの時点で、即時衝突判定が欲しい
3) LateUpdate()
Physics.OverlapXXX関数
そこで、即時に衝突判定を得たい場合には、Physics.OverlapXXX系関数が使える。 Physics.OverlapBox()、Physics.OverlapSphere()、Physics.OverlapCapsuleの3つがある。ボックス形、球体形、カプセル形と、他のコライダー(ただし、物理演算が済んでいること)との衝突を得ることができる。 OnTriggerXXX()やOnCollisionXXX()を使わずに、また、次のPhysic物理演算完了を待つロジックを書かずに実装できる。 OverlapXXX系関数は、衝突したコライダー情報を返す。なお、Physics.CheckBox()など、CheckXXX系関数では、True/Falseのみを返す。衝突しているかのみを得たい場合はこちらを使おう。
NonAlloc付きの方がより良い
OverlapXXX系関数は、衝突したコライダー情報が可変の配列で返ってくるが、ガーベジコレクション処理(メモリ確保開放)による処理負荷の追加を回避するため、「NonAlloc」が付いている方の関数を使うとより完璧だ。 OverlapBoxNonAlloc()、OverlapSphereNonAlloc()、OverlapCapsuleNonAlloc()の3つがある。 関数の詳細、コードサンプルは、Unity公式マニュアルサイトを参照されたい。
なぜ、この記事を書いたのか?それは、私が、Update()関数内で動的にゲームオブジェクトを生成し、コライダーを付け、他のオブジェクトと当たっているか取得しようしたとき、当たっているのに、当たっていなかったからだ。そして、デバッグするとどうやら次のFixedUpdateの、後のOnCollisionEnter()まで、検知していないのか、となり(以下略)
OverlapXXXNonAlloc系関数を、Update()内のゲームロジックで使用して、即時、当たり判定を得る、この方法も使っていこう。