【Unity】物理演算で動かすオブジェクトが壁を貫通する問題と対策
『キング・クリムゾン』の能力では、この世の時間は消し飛び・・・・・・そして全ての人間は、この時間の中で動いた足音を覚えていないッ!
当たり判定を弾が貫通する
FPSや高速でプレイヤーが移動する系のゲームを作っている時、弾やプレイヤーがオブジェクトを貫通する事があります。
例えば下のように、低速で動く黄色い玉は光る壁で停止していますが、高速で動く緑の玉は光る壁を無視して奥の壁まで到達しています。
この問題の理由は、物理演算の処理にあります。
物理演算で動かすオブジェクトは、実際にはテレポートで移動しており、その中間のフレームをFixedUpdateで分割した秒数で補完しています。
このため、補完してチェックする回数が十分で無い場合、上のGifアニメのように壁を貫通してしまう事があります。
上のGifアニメは分かりやすいようにFixedUpdateを増やしてチェック頻度を減らしていますが、実際のゲームでも弾が高速で動き過ぎると貫通したりします。
対策1(補完回数を増やす)
弾を貫通させたくない手っ取り早い方法は、FixedUpdateを増やして補完回数を増やす事です。これはゲーム進行における全ての物理演算に非常に重大な負荷を与えますが、精度は良くなります。
メニューバーのEdit>PlayerSettings>TimeよりTimeマネージャーを開き、FixedTimeStepの値を小さくします。最も雑な値で1(1秒に1回のチェック)、最も高精度な処理で0.0001(1秒間に1000回チェック)です。
物理演算を沢山使用している場合、一気に負荷が跳ね上がる事があります。
対策2(Collision Detectionを使う)
対策その2は、Collision Detectionを使用する事です。
Collision Detectionを使用すると、高速で動くコライダーが突き抜けなくなります。
このContinuesとContinues Dynamicの違いは、接触する相手にRigidbodyがあるか無いかです。
Rigidbodyのある(動くコライダーを持つ)オブジェクトに対して連続衝突判定を行う場合、移動先も予測して接触判定を行う必要があります。その為、計算が非常に複雑になります。
逆にRigidbodyの無い(動かないコライダー)に対して連続衝突判定を行う場合、オブジェクト配置時に作成・結合した当たり判定に対して処理を行えば良いので、安いです。
Collision DetectionとFixedTimeStep
なお、Collision Detectionは移動先に接触判定する要素があるかどうかのチェックをする物なので、厳密に正しい動きをしている訳では無いみたいです。
物理的に正しい挙動を期待するならFixed Updateの回数を増やすのが良いみたいです。逆に、パフォーマンスが欲しいなら必要最低限なオブジェクトにCollision Detection のcontinues dynamicを付けるのが良さそうです。
Raycastを使用する
もう一つのアプローチは、Rigidbodyとか使わずRaycastを使用する方法です。それが「目にも止まらぬ速さで飛ぶ」ならば、これを使ったほうが良いです。
SphereCast等を使えば、ある程度幅のある判定も行えます。