Unity - 2D物理演算と衝突判定メモ


Unityを使う以上、エンジンにある機能を使っていきたい。
バージョンは2021.3。

エンジン機能をあまり使わない方向についても最後に書く。

物理演算の世界に参加する

Rigidbody2DCollider2D が参加券である。
対象のゲームオブジェクトに Rigidbody2D と いずれかのCollider2D の二つを付けるとUnityに物理演算してもらえるようになる。
Rigidbody2D が挙動を Collider2D 衝突を担当する。

物理演算ステップがいつ実行されるかは、Unityエディタの

  • Edit -> Project Setting -> physics 2D -> Simulation Mode

にて設定されており、デフォルトでは FixedUpdate() 実行後となっている。
例えばこれを Script とすると Physics2D.Simulate() をスクリプトで呼び出したときに物理演算ステップが実行される。

Rigidbody2D について

Rigidbody2D が付けられたゲームオブジェクトにおいては Transform を直接触らない。Rigidbody2D が移動すると、それが Transform へ伝搬する。
Rigidbody2DAddForce() によって動かす。速度を直接変更とかしない。物体に力を加えて加速させ、速度を上げて、移動する。そういうふうに動かす。
Rigidbody2D への操作は FixedUpdate() 内で行う(Simulation ModeFixedUpdata の場合)、そうすると次の物理演算ステップに反映される。

単純な AddForce() の場合、物理演算ステップ中ずっと力が加えられる、ような動作になると思う。そのため、固定周期でない Updata() などで行うと意図しない動作となりやすい。
瞬間的な力を表す ForceMode2D.Impulse であれば、おそらくその限りではない。

Rigidbody2D が物理演算によって動くのは Body TypeDynamic の場合のみである。
Static の場合は固定され動かないし、Kinematic であればスクリプトが挙動を決める。

一時的に物理演算からはずれたい時は Simulated を Off にする。
Simulated が Off な Rigidbody2D は物理演算から無視される。動かなくなるし、他の Rigidbody2D とも干渉しない(衝突しなくなる)。

Collider2D について

Collider2DRigidbody2D に紐付けられる。Rigidbody2D がない場合は、Body TypeStaticRigidbody2D として扱われる。
Collider2D にはいくつか種類があって、自由形状のものは処理が重くなる。
一つの Rigidbody2D には 複数の Collider2D を紐づけできるので、基本図形の組み合わせで複雑な形に対応させることも可能。
衝突すると OnCollisionXXX2D() が呼び出される。

Is Trigger を On にすると、他の Collider2D と衝突/反発 しなくなり、重なる/すり抜けるようになる。 この時、OnTriggerXXX2D() が呼び出される(OnCollisionXXX2D() は呼ばれない)。
特定の地点を通過したらイベント発生とか、そういう使い方をするのだろうか。
ある領域においては速度が1/2になる、みたいなのも作れそうだが、そういう用途向けには Effector2D があって、よくある動作が用意されている。

衝突対象は、Layer(レイヤ) 単位で指定する。
例えば、自キャラ(操作キャラ)と敵キャラは衝突するが敵キャラ同士は衝突しない、とする場合、 自キャラレイヤと敵キャラレイヤをそれぞれ用意し、敵キャラレイヤ-敵キャラレイヤ間は衝突をOffにすればよい。 Unityエディタの

  • Edit -> Project Setting -> physics 2D -> Collision Layer Matrix

でそういう設定を行う。

スクリプトから動的に変更するような他の手段などは見つからなかったので不明。
敵キャラと接触しダメージを受けたら一定時間無敵になる、などしたい場合は、無敵時間レイヤを用意してレイヤを移動すれば良い。
(Is Trigger を On にする等だと、壁抜けしてしまうので期待する動作にならない)

OnCollisionXXX2D()OnTriggerXXX2D() などのイベント通知系のメソッド内部で重い処理をするのはやめた方が良い気がするので、 これらの中では一時リストへの情報登録などに留め、実際の処理は、次の物理演算ステップ前に行うようにする。
もしくは、OnCollisionOnTrigger系は使わず、GetContacts()OverlapCollider() を用いれば、スクリプトの好きな個所で同じ情報を取得できる。 当たり前だが、次の物理演算ステップが実行されたら更新されるので、それより前に呼び出す。

エンジン側からのスクリプト呼び出し回数は少ない方がパフォーマンス的に有利らしいので、こちらの方が良いかもしれない

古い記事なので、いまでもそうかは分からない。

エンジン機能をあまり使わない方向

Simulation Mode を Script にし Physics2D.Simulate() を呼び出さなければ、物理演算自体が停止する。 この場合は Collider2DRigidbody2D 共に働かないので、全てを自前で実装しなければならない。

衝突判定は欲しいなら、Rigidbody2DBody TypeKinematic に変更し、Use Full Kinematic Contacts を On にする。 衝突/反発等 しなくなるが OverlapCollider() で重なっている Collider2D を取得できるので、それを使って該当処理を実装すればよい。
Use Full Kinematic Contacts が Off だと、Kinematic-Kinematic および Kinematic-Static 間が干渉しなくなり、お互いの Collider2D に反応しなくなる。

以上。

unity