MRが楽しい

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

MRTKv2.xでレイの衝突先が空間メッシュか否か判定する

本日はMRTKv2.xの小ネタ枠です。
MRTKv2.xでレイの衝突先が空間メッシュか否か判定する方法を記事にします。

前提条件

本記事の動作確認を実施するため、以下の前回記事の環境を利用します。
bluebirdofoz.hatenablog.com

レイの衝突先が空間メッシュか否か判定する

Physics.Raycast関数を利用してレイが衝突したオブジェクトのコライダーからレイヤー番号を取得することができます。
docs.unity3d.com

if (Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), out hit, Mathf.Infinity))
{
    Debug.Log("layer : {hit.collider.gameObject.layer}");
}

また空間メッシュのレイヤー番号はスクリプトから以下の方法で取得できます。
レイヤー番号がこれと一致するかチェックすることでレイが衝突したオブジェクトが空間メッシュか判定できます。
bluebirdofoz.hatenablog.com

// 空間認識のオブザーバを取得する
IMixedRealitySpatialAwarenessMeshObserver SpatialAwarenessMeshObserver =
    CoreServices.GetSpatialAwarenessSystemDataProvider<IMixedRealitySpatialAwarenessMeshObserver>();

// オブザーバからレイヤー番号を取得する
int spatialAwarenessLayer = SpatialAwarenessMeshObserver.MeshPhysicsLayer;

サンプルスクリプト

現在のカメラ視点からレイを飛ばし、衝突したオブジェクトが空間メッシュか否か、または衝突しなかったか判定するサンプルスクリプトを作成しました。
・SpatialMapTrackingPointCheck.cs

using Microsoft.MixedReality.Toolkit;
using Microsoft.MixedReality.Toolkit.SpatialAwareness;
using Microsoft.MixedReality.Toolkit.Utilities;
using UnityEngine;

public class SpatialMapTrackingPointCheck : MonoBehaviour
{
    [SerializeField] Renderer _renderer = default;
    
    private enum HitType
    {
        Nothing, // 何も当たらなかった
        SpatialMap, // 空間マップに当たった
        Other // その他のオブジェクトに当たった
    }
    
    void Update()
    {
        UpdateTransformFromCamera();
    }
    
    /// <summary>
    /// カメラから一定距離になるように位置を更新する
    /// </summary>
    private void UpdateTransformFromCamera()
    {
        // カメラからの距離上限(5m)
        const float modelSetDistance = 3.0f;
        
        // カメラ前方の位置を計算
        var camTransform = CameraCache.Main.transform;
        var camPosition = camTransform.position;
        var targetPosition =
            camPosition +
            camTransform.forward *
            modelSetDistance;
            
        // Spatial Awarenessのレイヤー(31: Spatial Awareness)に対してのみレイキャストの判定を行う
        // 空間認識のオブザーバを取得する
        IMixedRealitySpatialAwarenessMeshObserver spatialAwarenessMeshObserver =
            CoreServices.GetSpatialAwarenessSystemDataProvider<IMixedRealitySpatialAwarenessMeshObserver>();

        // オブザーバからレイヤー番号を取得する
        int meshPhysicsLayer = spatialAwarenessMeshObserver.MeshPhysicsLayer;
        
        // カメラからレイキャストを飛ばして途中に障害物があれば、その座標と種類を取得する
        HitType hitType = HitType.Nothing;
        if (Physics.Raycast(camPosition, camTransform.forward, out var hit, modelSetDistance))
        {
            targetPosition = hit.point;
            if (hit.collider.gameObject.layer == meshPhysicsLayer)
            {
                hitType = HitType.SpatialMap;
            }
            else
            {
                hitType = HitType.Other;
            }
        }
        
        // 計算した位置を反映する
        transform.position = targetPosition;

        // オブジェクトをカメラの方向に向ける
        transform.LookAt(camPosition);
        
        // 衝突したオブジェクトの種類によって色を変える
        _renderer.material.color = hitType switch
        {
            HitType.Nothing => Color.white,
            HitType.SpatialMap => Color.red,
            HitType.Other => Color.green
        };
    }
}

スクリプトをゲームオブジェクトにアタッチすると、カメラの視点の正面3m以内に障害物が存在すれば、その位置にオブジェクトが追従します。

シーンを再生してシミュレーション上で動作を確認します。
空間メッシュの方向を向くとポイントオブジェクトが赤色に、それ以外のオブジェクトだと緑色に、衝突判定が撮れなかった場合は白色になります。