個人的勉強メモ置き場

プログラミングど素人のメモ置き場

色々な接地判定

キャラクターの移動時、地面に接触しているかどうかを判定したいときがある

以前までは地面となるオブジェクトにtagをつけてOnConllisionEnterなどで判定していたけど、他にも色んな判定方法があるらしい。

CharacterControllerを使う

CharacterControllerにはあらかじめ接地判定用にisGroundedプロパティが存在する
docs.unity3d.com
これだけ見るとすごく楽ちんなんだけど実は判定制度が微妙らしい

f:id:zykb:20200610115553g:plain

斜面だとfalseになることがある。厳密な判定が必要なら他の方法を使ったほうがよさそう

Physics.RayCastを使う

ようは真下にレイを飛ばして衝突していれば接地していると判断する方法

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GroundCheck : MonoBehaviour
{
    public float rayDistance = 1.2f;

    void Update() 
    {
        Vector3 rayPosition = transform.position + new Vector3(0.0f, 0.1f, 0.0f);
        Ray ray = new Ray(rayPosition, Vector3.down);
        bool isGrounded = Physics.Raycast(ray, rayDistance);

        Debug.Log(isGrounded);
        Debug.DrawRay(transform.position + new Vector3(0.0f, 0.1f, 0.0f), 
                      Vector3.down * rayDistance, Color.red);
    }
}


f:id:zykb:20200611011027p:plain

精度は上がるけどレイの長さを調節したり少しめんどくさいのと、地面にのみ衝突させるときはレイヤーを設定する必要がある

Physics.CapsuleCastを使う

SphereCastやBoxCastでもいいけど今回はカプセル型のオブジェクトを置いているのでCapsuleCastを使用
RayCastを太くしたようなものなのでよりいい感じに判定できるらしい

public class GroundCheck : MonoBehaviour
{
    RaycastHit hit;
    Vector3 startPos;
    Vector3 endPos;
    bool isGrounded;

    void Update() 
    {
        startPos = transform.position - transform.up * 0.5f;
        endPos = transform.position + transform.up * 0.5f; 
        isGrounded = Physics.CapsuleCast(startPos, endPos, 0.5f, transform.up * -1.0f, out hit);
        Debug.Log(isGrounded);
    }

    void OnDrawGizmos()
    {
        Gizmos.color = Color.red;
        if(isGrounded)
        {
            Gizmos.DrawRay (transform.position, transform.up * -1 * hit.distance);
            Gizmos.DrawWireSphere(startPos + transform.up * -1 * hit.distance, 0.5f);
            Gizmos.DrawWireSphere(endPos + transform.up * -1 * hit.distance, 0.5f);
        }
        else
        {
            Gizmos.DrawRay (transform.position, transform.up * -1 * 100);
        }
    }
}

f:id:zykb:20200611040343g:plain

こんな感じで内部にめり込んでるときはfalseになるので、どうにかしたい場合にはcast位置を上げるかCheckSphereなどを使うといいらしい

おわりに

調べると他にも色々あるみたいなので、状況に応じて最適な接地判定が出来るようになりたいですね