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

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

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個。
とにかく多いわけだが、簡単なものだとこのように書ける。

```C#
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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/231914/b1f41dea-d940-0248-880a-aff336e0b0d4.png)


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

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

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

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

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


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

# 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 の当たり判定、完全に理解した（）**

<P>
<P>
<P>
<P>

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

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

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

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

というオチ

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