Unity3DでCharacterControllerを使わずに自分なりに納得いく接地判定を作る
Unity 2020.2.7
キャラクターがジャンプや落下中などに2段ジャンプしないように接地判定を実現しようと思った時、最初に上がる候補としてCharacterControllerのIsGroundedが挙がる。
しかし色んな記事を参考にするとCharacterControllerの接地判定はイマイチらしく、ShereCastを使うといいよという記事が出てくる。
が、
実装について触れている記事があまり無かったため、自分なりに納得行く接地判定を作ったというメモ。
やりかた
以下のスライムモデルを参考に説明。
以下のスクリプトをモデルのゲームオブジェクトにアタッチ
using UnityEngine; public class GroundCheck : MonoBehaviour { public bool isGrounded; float radius = 0.1f; RaycastHit hit; void Update() { isGrounded = Physics.SphereCast( GetRayStartPosition(radius), radius, Vector3.down * GetMaxDistance(), out hit, GetMaxDistance() ); Debug.Log(isGrounded); } void OnDrawGizmos() { Gizmos.color = Color.red; Gizmos.DrawWireSphere(GetRayStartPosition(), radius); Gizmos.color = Color.blue; Gizmos.DrawRay(GetRayStartPosition(radius), Vector3.down * GetMaxDistance()); } Vector3 GetRayStartPosition(float offset = 0) { Vector3 t = transform.position; t.y += offset; return t; } float GetMaxDistance() { return radius * 2; } }
するとモデルの真下に小さなGizmosが表示される。
簡単に説明すると、赤丸の範囲内にオブジェクトが重なっていればisGroundedがtrueになる。
(ちょうど赤丸の直径が青い線の部分だけどみづらい…)
着地の挙動が若干不自然なのは大目に見て(DOLocalJumpでジャンプ)
所感
いちおうレイヤー分ける必要はないのはメリットかもしれないけど、この判定方法はSphere Collider使っても同じことできそう…
3/14追記
new()しないほうがパフォーマンス的に良かったのでGetRayStartPosition()メソッドを書き換えた。
古いコードは以下の通り。
Vector3 GetRayStartPosition(float offset = 0) { return new Vector3(transform.position.x, transform.position.y + offset, transform.position.z); }