MRが楽しい

MRやVRについて学習したことを書き残す

RaycastHit.normalを参照してレイキャストが当たった面が水平面か判定する

本日は Unity の小ネタ枠です。
RaycastHit.normalを参照してレイキャストが当たった面が水平面か判定する方法についてです。

RaycastHit.normal

RaycastHit.normal はレイがヒットした面のノーマル(法線)が取得できます。
法線の向きをチェックすることで面が水平方向の面か垂直方向の面かを判定できます。
docs.unity3d.com

例えば以下のようにチェック可能です。

        // オブジェクト直下にレイキャストがヒットするか
        var ray = new Ray(transform.position, Vector3.down);
        var hit = Physics.RaycastNonAlloc(ray, _raycastHits, 1.0f);
        Debug.Log($"hitcount: {hit}");

        // 平面が検出できたか
        if (hit > 0)
        {
            // 平面の法線をチェックして法線が垂直上方向に向いていれば水平な床と判定する
            var hitNormal = _raycastHits[0].normal;
            var angle = Vector3.Angle(hitNormal, Vector3.up);
            _isHitHorizontalPlane = angle <= _approximatelyAngle;
        }

サンプルシーン

以下の水平な面と傾いた面が存在するサンプルシーンを作成しました。

ここに以下の直下の面が水平かチェックするコンポーネントを作成して、オブジェクトに設定します。
・RaycastCheckHorizontalTest.cs

using UnityEngine;

public class RaycastCheckHorizontalTest : MonoBehaviour
{
    [Header("検出結果")]
    /// <summary>
    /// 水平な床を見つけたか
    /// </summary>
    [SerializeField, Tooltip("水平な床を見つけたか")]
    private bool _isHitHorizontalPlane;
    
    [Header("検出条件")]
    /// <summary>
    /// 許容する角度の誤差
    /// </summary>
    [SerializeField, Tooltip("許容する角度の誤差")]
    private float _approximatelyAngle;
    
    private RaycastHit[] _raycastHits = new RaycastHit[1];
    
    void Update()
    {
        // オブジェクトの直下にレイキャストがヒットするか
        var ray = new Ray(transform.position, Vector3.down);
        var hit = Physics.RaycastNonAlloc(ray, _raycastHits, 1.0f);
        Debug.Log($"hitcount: {hit}");

        // 平面が検出できたか
        if (hit > 0)
        {
            // 平面の法線をチェックして法線が垂直上方向に向いていれば水平な床と判定する
            var hitNormal = _raycastHits[0].normal;
            var angle = Vector3.Angle(hitNormal, Vector3.up);
            _isHitHorizontalPlane = angle <= _approximatelyAngle;
        }
        else
        {
            _isHitHorizontalPlane = false;
        }
    }
}

シーンを再生して動作を確認します。
コンポーネントを設定したオブジェクトを移動すると、直下に水平な面があるときのみ[_isHitHorizontalPlane]がチェック状態になります。