LoginSignup
8
4

More than 3 years have passed since last update.

[Unity] 当たり判定について整理する

Last updated at Posted at 2021-02-05

当たり判定について振り返る。

Unityを使う場合において、当たり判定って最初にやるんだけど、
実はちゃんと説明されてない項目だと思う。

っていうかね、OnTrigger・OnCollision を真っ先に勉強するのがある意味 罠なわけですよ。

1. OnTrigger・OnCollision

まずこいつらに白羽の矢が立つが、こいつらには致命的に弱点がある。

RigidBody がついていないと、イベントが発生しない。

一度はやったことがあるのではないだろうか。
あれー、貫通したり接触してるのにイベントが発生しないぞ?ってやつ。

今更説明するまでもないが、
RigidBody(剛体)は、物理シミュレーションを行うコンポーネントだ。
これらは毎フレーム(Fixed Update) 重力や加えられた力によって、物理演算を行う。

Collider に RigidBody をつけたいパターンがどれほどあるか、と言われると、
物理演算のゲームでもない限り、実際のところあまりない。

しかしながら、OnCollision、OnTriggerを発生させるために、
泣く泣く RigidBody をつける人もいるんじゃないだろうか?

それは間違いと言ってもいい。

RigidBody は能動的に動くクラスだからこそ、
自動的にイベントが発生すべき、という考え方の方が正しいと思う。

RigidBody があるから、OnCollision等のイベントを使う。これが原則だろう。

ついでに書いておくと、これらのイベントで注意しておきたいのが、
物理演算であるので、これらはFixed Update タイミングで発生するという事である。
Updateではない。

Updateタイミングで、Destoryチェックしてるのに何故か抜けるぞ? などというプログラムを書くと、
一生ハマることになるので気を付けておきたい。

Unity のライフサイクルとはお友達になっておこう。
こいつと仲が良いかどうかで、物理やAnimationの理解度が格段に変わってくるはずだ。

イベント関数の実行順序 : https://docs.unity3d.com/ja/2018.4/Manual/ExecutionOrder.html

当たり判定の話に戻るが、他にどういった方法があるのか確認しておこう。

2. Physics.Raycast

これについてどこかで見たことがある人はいるだろう。

Rayを飛ばして、何らかの Collider と衝突するか調べる機能だ。

Physics.Raycast だが、とにかく引数のパターンが多い。その数16個。
とにかく多いわけだが、簡単なものだとこのように書ける。

var ray = Camera.main.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0f));
RaycastHit hit;

if (Physics.Raycast(ray, out hit, 100f))
{
    Debug.Log(hit.collider.gameObject.name);
};

この例だと、カメラの中央からRay(光線)を飛ばして、100m 以内にHitするコライダーがないか確認する。

このRayCastシリーズも実はたくさんあり、以下のようなものがある。

  • BoxCast
  • SphereCast
  • CapsuleCast

それぞれ、箱型、球体、カプセル型のRayを飛ばすことが出来る。
とにかく類似するものが多いので、リファレンスを直接見た方がいいだろう。
まあ適当にググっても沢山の情報は得られると思う。機能としては古くからある。

Physicsリファレンス : https://docs.unity3d.com/ja/current/ScriptReference/Physics.html

これらは文字通り、
光線を飛ばすので、必然的に FPS の弾丸の当たり判定などの使用に向く。

Rigidbody なんていらなかったんや!
物理で飛ばしたら、敵に当たったら跳ね返って地面に転がることになる。草。
(それはそれであり? まあ今回はそういう話ではない。)

Raycastは、Physicsクラスの中に内蔵されている。
これらは Rigidbody こそ使わないもののUnity の物理レイヤーの概念を使って計算される。

Project Settings で見るチェックボックスのアレと同様のイメージだと分かりやすい。

アレ↓
image.png

これらは物理レイヤーで制御されている為、
LayerMaskによるレイヤー分け処理をすることが出来る。

LayerMaskは、ビット計算による指定になるので、
初心者は特に見慣れないだろうが、これはこれで、こういうものだと覚えたほうが良いだろう。

レイヤーマスクを設定する(個人サイト様):https://www.ame-name.com/archives/5621

こういったビットシフトの計算は、
演算の中でも最速中の最速なので(割り算すると遅いとかそういう次元のお話)、
物理演算ではどうしても高速化したかったのだろう。

また脱線してしまったが、
このRayCastはかなりシンプルに扱えるので、かなり使いやすいだろう。
RaycastHit に当たった相手方の情報も返って来るので、相手の特定も容易だ。


で、
これだけで終わりかというと、もう一つある。

3. Bounds 構造体による AABB 判定

UnityEngine では、Boundsという構造体が、

  • Collider.bounds
  • Mesh.bounds
  • Renderer.bounds

で、それぞれ活用されている。(リファレンスからのパクリ)

このBoundsというやつは六面体を表現しており、
ざっくりこの範囲使ってるよ、という表現で使われている。
とにかく高速に重なっていないかを判断することが出来る。それがAABBというやつだ。

[Unity] バウンディングボックス(AABB)について調べてみた(個人サイト様):http://ftvoid.com/blog/post/1504

如何にも当たり判定で使いそうだが、Meshでも使っているのはどういうことか?

Mesh はカメラの外にある場合、レンダリングしない。
どうやって判断しているかというと、カメラが持っている視錐台と各Meshが持っているBoundsを比較するのだ。

まあ今回はこういったカメラの仕組みは関係ないが、
Collider を使わずとも、Bounds同士を判定すれば、
重なりを判定することが出来る。

bounds には、Intersects という関数が用意されているおり、
これで相手のBounsを指定するだけで重なりを判定してくれる。

Mesh.bounds.Intersects( 相手のBounds ) と記載すれば、Boolで当たっているかどうかを判定してくれる。

何でも用意してくれているだぜ。やったぜ。

ただし、Sphere Collider(球体) であっても持っているBoundsは四角だし、
Mesh が持っているBoundsは、あくまで描写するかしないかの判定のために使う為、
マージンが大きめに取られているので、Mesh のBoundsで当たり判定をやろうというのは無謀だろう。

大人しくBoxColliderをつけるか、Bounds を内部的に定義しておくのが良いだろう。
自前で Bounds を定義しても、当然Editor上は見えないので、
Gizmoを書くなどして使いやすくしたほうが良いだろう。

余談ではあるが、Mesh Collider というやつがいる。
こいつは、メッシュの形状をしたコライダーなわけだが、
Bounds で計算して True 判定をした後に、メッシュのすべての頂点について判定を行わなければいけない為、
爆発的に計算量が増えて物理演算が爆発することがある。
Mesh Colliderはまあやめておけ。なるべく各種Colliderを組み合わせて代用しよう。

まとめ?

用途によってこれらを使い分けられれば、君も当たり判定マスターだ!

Unity の当たり判定、完全に理解した()




4. Extra : ゴリラ化しようぜ。Are You Gorilla? We Are Gorilla !!

っていうか、いい加減 ECS 使おうぜという話。

当たり判定をBoundsでやるのはいいけど、
これって位置計算だけだからメインスレッドでやる必要なくね?

まあ自分でやるのはいつ頃になるか…。
I`m not Gorilla...

というオチ

眠たくなりながら書いたので誤字がめっちゃあった。修正した。

8
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
4