今回はUnity初心者の方向けの話題で、
を丁寧に解説するという内容になっています。
アクションゲームを作っていると、まず確実にジャンプなどのアクションを実装することになると思うのですが、その際に「地面をどうやって検知するか?」という接地判定の問題が出てきます。これは単純にコライダーの当たり判定に頼ってもうまくいかないので、初心者の方にとってはなかなか悩ましい問題なのではないでしょうか。
そこでここでは、具体的なC#スクリプトを交えながら
- なぜ接地判定の処理を作る必要があるのか?
- 具体的にどうやって作ればいいのか?
といった点を丁寧に説明していきますね。
なぜわざわざ接地判定の処理を作る必要があるのか?
でははじめに、そもそもなぜ接地判定の処理を作る必要があるのかを説明しておきます。
まずUnity初心者の方からすると
- キャラクターに付けたコライダーに当たり判定があるんだから、それを接地判定として使えばいい(つまり、コライダーに何かヒットしたら接地したと判定すればいい)のでは?
- それならわざわざ接地判定の処理を作る必要がないじゃん
と思うかもしれません。
しかし単純にコライダーを接地判定に使う場合は「コライダーのどの面で接触したか?」を判断できないという問題があります。つまりこの方法だと例えば地面に当たろうが壁に当たろうが区別できないので、壁と接触したときでも接地したと誤判定されてしまうのです。
まあ、どうしてもコライダーを使いたい場合は足元に「接地判定用の小さいコライダー」を子オブジェクトとしてつける方法もありますが…
これだと例えば下のような状況(地面の角に差し掛かった時など)に対応できません。
このようなことを考慮すると別途接地判定の処理を作ったほうがもっと高精度な判定ができるのです。
接地判定処理にはRaycastを使おう
さて接地判定処理の必要性をご理解頂いたところで、次にUnityで接地判定処理を作る際によく使われる方法をご紹介します。接地判定のやり方は何通りかあるのですが、個人的に一番オススメなのが
Raycast(レイキャスト)を使う方法
です。
Raycastとは簡単に言えば「目に見えない光線(=レイ)を発射し、光線の通り道にあるコライダーを検知する機能」です。これを使うメリットとしては次のようなものが挙げられます。
- レイは特定のゲームオブジェクトだけに当たるように設定できる
- レイは線・立方体・球形・矩形・円形など予め用意された形状を利用できる
- レイの発射位置や発射距離はその都度変更することができる
これにより他の方法よりも柔軟で高精度な処理を実現できます。接地判定を実装するときはとりあえずRaycastを使っておけばまず間違いありません。
なおRaycastについては下記の記事で詳しく説明していますので、そちらも併せてご覧頂ければと思います。
Raycastを使う場合の処理の考え方
さてそんなRaycastですが、文章だけで説明してもピンとこない方もいらっしゃるかと思いますので図を使って接地判定処理の考え方を説明しておこうと思います。
まず基本的な考え方としては、レイをキャラクターの足元の位置から下方向に一定距離だけ飛ばします。
このとき
- 予め地面用のレイヤーを用意しておき、レイがそのレイヤーにだけ当たるようにする(そうしないとプレイヤーのコライダーに当たってしまったりする)
- レイの距離は適当に決めてよいが、短すぎたり長すぎたりしないようにする
のがポイントです。
ところで先ほどの図のように直線状のレイだと、地面の角に差し掛かった時などに上手く接地判定を行えない場合がありますよね。そこで下の図のように形状を持ったレイを使うとより高精度な判定を行うことができます。
3Dゲームの場合はSphereCastという球状のRaycast、2Dゲームの場合はCircleCastという円形のRaycastを使えるのでそれぞれ覚えておきましょう。
最後に注意点として、Raycastは発射開始地点の座標のコライダーは検出しないので注意が必要です。つまり、レイを発射する地点は足元ぴったりではなく足元の少し上に設定しておく必要があります。
Raycastを使った接地判定処理のC#スクリプト
ではRaycastを使った接地判定の具体例としてサンプルスクリプトを掲載しておきます。
3Dアクションゲームの場合
using UnityEngine; public class GroundCheck3D : MonoBehaviour { [SerializeField] float groundCheckRadius = 0.4f; [SerializeField] float groundCheckOffsetY = 0.45f; [SerializeField] float groundCheckDistance = 0.2f; [SerializeField] LayerMask groundLayers = 0; RaycastHit hit; bool CheckGroundStatus() { return Physics.SphereCast(transform.position + groundCheckOffsetY * Vector3.up, groundCheckRadius, Vector3.down, out hit, groundCheckDistance, groundLayers, QueryTriggerInteraction.Ignore); } }
ここでは先ほどご紹介したSpherecastを使っています。接地判定処理用の関数である「CheckGroundStatus」は戻り値がbool型になっており、呼び出されると今この瞬間接地しているかどうかを返します。もし
- どのゲームオブジェクトと接地しているか
- 接地している地点の法線
などを取得したい場合は、変数hitにRaycastの結果が格納されるようになっているのでそれを使ってください。
なおSpherecastについては公式リファレンスをご覧ください。
2Dアクションゲームの場合
using UnityEngine; public class GroundCheck2D : MonoBehaviour { [SerializeField] float groundCheckRadius = 0.4f; [SerializeField] float groundCheckOffsetY = 0.45f; [SerializeField] float groundCheckDistance = 0.2f; [SerializeField] LayerMask groundLayers = 0; RaycastHit2D CheckGroundStatus() { return Physics2D.CircleCast((Vector2)transform.position + groundCheckOffsetY * Vector2.up, groundCheckRadius, Vector2.down, groundCheckDistance, groundLayers); } }
2Dゲーム用の接地判定ではCircleCastを使うとよいかと思います。こちらは戻り値がRaycastHit2Dとなっている点にご注意ください。
なおCircleCastについての詳細は公式リファレンスをご覧ください。
おわりに
以上、簡単ではありますがUnityで接地判定を実装する方法についてご説明しました。
接地判定は地味な処理ではありますが、アクションゲームをはじめ様々なゲームで避けて通れない必須の処理となります。ぜひ上記の内容を参考にしていただき作り方を身につけて頂ければと思います。
この記事がUnityでのゲーム開発のお役に立てば幸いです。