poi’s tech blog

3D多人数同時接続型球体アクション成人向けゲーム開発のためのアイデア、ナレッジ

Unity3DでCharacterControllerを使わずに自分なりに納得いく接地判定を作る

Unity 2020.2.7

キャラクターがジャンプや落下中などに2段ジャンプしないように接地判定を実現しようと思った時、最初に上がる候補としてCharacterControllerのIsGroundedが挙がる。

しかし色んな記事を参考にするとCharacterControllerの接地判定はイマイチらしく、ShereCastを使うといいよという記事が出てくる。

が、

実装について触れている記事があまり無かったため、自分なりに納得行く接地判定を作ったというメモ。

やりかた

以下のスライムモデルを参考に説明。

f:id:poipoipoip:20210312223352p:plain
かわいい。

f:id:poipoipoip:20210312223527p:plain
モデル(transform)の原点は高さ0

以下のスクリプトをモデルのゲームオブジェクトにアタッチ

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が表示される。

f:id:poipoipoip:20210312223812p:plain
ギズモ

簡単に説明すると、赤丸の範囲内にオブジェクトが重なっていればisGroundedがtrueになる。
(ちょうど赤丸の直径が青い線の部分だけどみづらい…)

f:id:poipoipoip:20210312224523g:plain
ぴょんぴょん

着地の挙動が若干不自然なのは大目に見て(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);
    }