序
当たり判定について振り返る。
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 で見るチェックボックスのアレと同様のイメージだと分かりやすい。
これらは物理レイヤーで制御されている為、
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...
というオチ
眠たくなりながら書いたので誤字がめっちゃあった。修正した。