1. はじめに
こんにちは、azarashin です。 ゲームではしばしば当たり判定処理によってゲームの進行を変化させるシーンが登場します。とりわけアクションゲームやシューティングゲームのようなジャンルでは当たり判定は必要になります。、「当たり判定の計算にも様々な方法あり、古典的なゲームであれば中学で習った三平方の定理で判定するような単純なケースもあれば、高校数学以上のベクトル計算を駆使しないと判定できないような複雑なケースも存在します。 Unityでは様々な状況で当たり判定を行う機能としてCollider(コライダ)という機能があります。 本記事ではこのコライダについて紹介していきたいと思います。
2. 当たり判定の形状について
Unityで当たり判定処理を行なうには、2つのオブジェクトが互いに重なっているかどうかをコライダを用いて判定します。 この時、オブジェクトの形状をそのままの形状で当たり判定処理を行なうと計算時間がより多くかかってしまい、描画性能、そしてゲーム性に大きな影響を与えてしまいます。そこでUnityでは見た目の形状とは別に単純な形状による当たり判定処理を行なうことができるようになっています。 当たり判定の対象にできる形状にはいくつかの種類があり、それぞれの形状に対してコライダが定義されています。
コライダの種類 | 備考 |
---|---|
Sphere Collider | 球 |
Box Collider | 直方体 |
Capsule Collider | カプセル型 |
Wheel Collider | 陸上車両用の特殊なコライダ |
Terrain Collider | 地形(Terrain)を元にしたコライダ |
Mesh Collider | オブジェクトの形状に合わせたコライダ |
当たり判定に多少のずれを許容できる場合はSphere Collider, Box Collider, Capsule Collider を組み合わせるのが性能面で好ましいでしょう。
一方、なるべく元のオブジェクトの形状に合わせた当たり判定を実現したい場合はMesh Collider を使用しましょう。
3. 当たり判定の形状確認
Gizmos を有効にすると、オブジェクトの形状に対する実際の当たり判定、即ちコライダの範囲がどのようになっているかがわかります。
例えばShpereCollider のように、当たり判定を調べたいコライダがGizmos のリストの中に表示されているはずですので、このチェックが入っていることを確認しましょう。チェックが外れているとGizmos としてコライダの範囲が表示されなくなります。
また、コライダと本来のオブジェクトの形状とが重なっているとコライダ部分を確認しづらくなりますので、Inspector を確認して一時的に対象のオブジェクトに付与されているMesh Renderer のチェックを外してみましょう(確認が終わったら戻しておきましょう)。
するとコライダの部分だけが緑の線で見えるようになります。今回はShpereCollider を使用していますので球体の形状をしたコライダを確認できるはずです。
メッシュコライダの場合は見た目も複雑になります。
例えば下記のフリーアセットに登場する岩のオブジェクトではMesh Collider が割り当てられています。
この岩オブジェクトに対してコライダを可視化すると下記のようになります。
ご覧のようにコライダの形状が複雑になりますので当たり判定の精度も向上しますが、その分計算負荷が増えて描画性能に影響を大きく与えますので、乱用は避けるようにしましょう。
4. コライダのサイズ変更・スケーリング
ここで注意すべき点があります。オブジェクトのScale を変化させた時、縦横奥比を変えてしまうとコライダとオブジェクト形状とがずれてくることがあります。 SphereCollider の場合はオブジェクトを縦長にしてもコライダ部分は球体のままです(大きさは変化する)。
一方、BoxCollider の場合はオブジェクトを縦長にするとコライダも縦長になります。
当たり判定を求めるときはコライダの種類とオブジェクトのスケール値に対してコライダの形状がどのようになっているのか確認し、想定外の形状にならないよう注意しましょう。
5. 当たり判定を検出する
コライダの形状についての説明も進んできましたので、今度は実際に当たり判定を検出する動作を確認してみましょう。 C#スクリプト SampleCollider.cs を作成し、下記のようにコードを記述してください。
using UnityEngine; public class SampleCollider : MonoBehaviour { private Material _material; private void Awake() { _material = GetComponent<MeshRenderer>().material; _material.SetColor("_BaseColor", Color.white); } void OnTriggerEnter(Collider other) { // 接触した直後 _material.SetColor("_BaseColor", Color.red); UnityEditor.EditorApplication.isPaused = true; } void OnTriggerStay(Collider other) { // 接触している間 _material.SetColor("_BaseColor", Color.yellow); } void OnTriggerExit(Collider other) { // 接触しなくなった直後(離脱後) _material.SetColor("_BaseColor", Color.green); UnityEditor.EditorApplication.isPaused = true; } void OnCollisionEnter(Collision collision) { // 接触した直後 _material.SetColor("_BaseColor", Color.blue); UnityEditor.EditorApplication.isPaused = true; } void OnCollisionStay(Collision collision) { // 接触している間 _material.SetColor("_BaseColor", Color.cyan); } void OnCollisionExit(Collision other) { // 接触しなくなった直後(離脱後) _material.SetColor("_BaseColor", Color.magenta); UnityEditor.EditorApplication.isPaused = true; } }
続いて床と球体を設置し、それぞれに対してコライダとRigidbody を割り当てます。 (コライダはオブジェクトを生成した時点で自動的に割り当てられていると思います。)
この時、コライダのIs Trigger とRigidbody のUse Gravity、Constraints をそれぞれ設定して動作にどのような違いが出るかを見てみましょう。
まずコライダのIs Trigger とRigidbody のUse Gravity、Constraints を下記のように設定してみます。
オブジェクト | 位置固定するか? | Is Trigger | コライダ |
---|---|---|---|
球 | 落下 | Off | SphereCollider |
床 | 固定 | Off | BoxCollider |
位置固定については、落下の場合はUse Gravity にチェックをいれ、Constraints のチェックをすべて外します。 逆に、固定の場合はUse Gravity のチェックを外し、Constraints のチェックをすべて入れます。
以上の設定を行ってエディタを再生すると下記のようになります。
ボールが床に接触した後、ボールが床の上を転がっていくのが確認できました。 該当するスクリプトを見てみると下記の部分が呼び出され、接触前・接触直後・接触中・離脱後とでそれぞれ色が白(white)→青(blue)→水色(cyan)→紫(magenta)と変化していることが確認できます。
void OnCollisionEnter(Collision collision) { // 接触した直後 _material.SetColor("_BaseColor", Color.blue); UnityEditor.EditorApplication.isPaused = true; } void OnCollisionStay(Collision collision) { // 接触している間 _material.SetColor("_BaseColor", Color.cyan); } void OnCollisionExit(Collision other) { // 接触しなくなった直後(離脱後) _material.SetColor("_BaseColor", Color.magenta); UnityEditor.EditorApplication.isPaused = true; }
続いてコライダのIs Trigger とRigidbody のUse Gravity、Constraints を下記のように設定してみます。
オブジェクト | 位置固定するか? | Is Trigger | コライダ |
---|---|---|---|
球 | 落下 | On | SphereCollider |
床 | 固定 | Off | BoxCollider |
前回との違いは球のIs Trigger をOff からOn に切り替えたことです。 この設定でエディタを再生すると下記のようになります。
ボールが床に接触した後、ボールが床を貫通して落下し続けていくのが確認できました。 該当するスクリプトとデバッグログを見てみると下記の部分が呼び出され、接触前・接触直後・接触中・離脱後とでそれぞれ色が白(white)→黄色(yellow)→緑(green)と変化していることが確認できます。
void OnTriggerEnter(Collider other) { // 接触した直後 _material.SetColor("_BaseColor", Color.red); Debug.Log("Red"); UnityEditor.EditorApplication.isPaused = true; } void OnTriggerStay(Collider other) { // 接触している間 _material.SetColor("_BaseColor", Color.yellow); Debug.Log("Yellow"); } void OnTriggerExit(Collider other) { // 接触しなくなった直後(離脱後) _material.SetColor("_BaseColor", Color.green); Debug.Log("Green"); UnityEditor.EditorApplication.isPaused = true; } }
Is Trigger にチェックを入れた場合、接触した直後に赤色に変わったのが観測されず、いきなり黄色に変化していますが、 これは1フレームのレンダリングの中でマテリアルの色が赤→黄色と変化したためです。
このことは1フレームずつ再生したときにデバッグログが以下のように出力されていることから確認することができます。 ("Red"が2回表示されているのは球と床それぞれでOnTriggerEnter が呼び出されているためです。)
以上のように、コライダ同士が
それぞれのタイミングで各種メソッドが呼び出されていることがわかります。 また、Collider コンポーネントのIs Trigger にチェックを入れるかどうかでOnTrigger~又はOnCollision~のどちらかが呼び出されていることもわかります。
終わりに
本記事では様々な状況で当たり判定を行うコライダというコンポーネントについて紹介しました。 当たり判定処理はゲームジャンルによっては避けては通れない処理ですが、それなりに処理負荷のかかる処理でもあります。 当たり判定の精度と描画性能のバランスを常に考えながら、開発中は動作確認をしながら違和感のない操作ができているかどうかを定期的に確認することが重要です。 本記事の内容を活用して是非楽しいゲームを制作してみてください。