さっそくですがデモです。
Unityちゃんがレーザービームを照射した箇所に
壁に対して垂直に立って現れます。

検出した平面に対して、
Unityちゃんが垂直に表示されるような処理を実装しました。

使い方はとても簡単ですが、知らないと実装するまで少々時間を使います。
ですので、どなたかのお役に立てるようメモを残しておきます。


バージョン情報

Unity 2019.3.4f1
Lumin OS v0.98.10
Lumin SDK 0.24.1


下準備

ビルドまでの環境構築は下記リンク参照です。 
https://gaprot.jp/2020/05/11/magicleap-dev-environment/

まずはControllerのPrefabをHierarchyに配置します。
下記の場所にPrefabはあります。
Assets/MagicLeap/Examples/Assets/Prefabs



次にControllerのPrefabを解除して、
子階層から余分なオブジェクトを削除します。
最後に、画像のコンポーネントだけにします。


コード

実際のコード全文がこちらです。
これだけであとは表示したいモデルさえ用意すればもうなにもいりません。

using UnityEngine;
using UnityEngine.XR.MagicLeap;

/// <summary>
/// 現実空間の物体にRayが衝突するデモ コントローラーにアタッチ
/// </summary>
[RequireComponent(typeof(LineRenderer))]
public class WorldCollisionBeam : MonoBehaviour
{
    //表示するオブジェクト
    [SerializeField] private GameObject _pointerObject;
    
    //レーザーの長さ
    [SerializeField] private float _laserDistance = 10f;

    private float _lineWidth = 0.001f;

    private MLRaycast.QueryParams _raycastParams = new MLRaycast.QueryParams();

    private LineRenderer _laserLineRenderer;

    private Transform _controllerTransform;
    
    private void Start()
    {
        _laserLineRenderer = this.gameObject.GetComponent<LineRenderer>();
        
        MLResult result = MLRaycast.Start();
        
        if (!result.IsOk)
        {
            Debug.LogError("エラー : 何らかの理由でRayは出てません");
            return;
        }
        
        //レーザーの太さ
        _laserLineRenderer.startWidth = _lineWidth;
    }

    private void Update()
    {
        _controllerTransform = this.gameObject.transform;
        
        //可視光線の始点
        _laserLineRenderer.SetPosition(0,_controllerTransform.position);
        
        //Rayの位置をコントローラーの向きに連動
        _raycastParams.Position = _controllerTransform.position;
        _raycastParams.Direction = _controllerTransform.forward;
        _raycastParams.UpVector = _controllerTransform.up;

        //変更したパラメーターとハンドラーを渡す
        MLRaycast.Raycast(_raycastParams, HandleOnReceiveRaycast);
    }
    
    /// <summary>
    /// コントローラーのレーザーポインターと平面が衝突したか検出
    /// </summary>
    /// <param name="state">レーザーの状態(当たり判定)</param>
    /// <param name="point">衝突した座標</param>
    /// <param name="normal">衝突した面の法線</param>
    /// <param name="confidence">面の認識率</param>
    private void HandleOnReceiveRaycast(MLRaycast.ResultState state, Vector3 point, Vector3 normal,float confidence)
    {
        //当たっていたらその場所が可視光線の終点
        if (state == MLRaycast.ResultState.HitObserved)
        {
            _laserLineRenderer.SetPosition(1,point);
            
            //オブジェクトの位置合わせ
            _pointerObject.transform.rotation = Quaternion.FromToRotation(Vector3.up, normal);
            _pointerObject.transform.position = point;
            
            Debug.Log("Hit");
        }
        //当たってなければ任意の長さの場所が終点
        else
        {
            _laserLineRenderer.SetPosition(1,_controllerTransform.forward * _laserDistance);
        }
    }
}

MLRaycast.Raycast

MLRaycast.Raycast
衝突した平面の情報を取得可能なRayの機能を担っています。

引数にはRayのパラメータ
実際に平面とRayが当たったときのコールバックを渡します。

まずはMLRaycast.Raycastに渡すRayのパラメータを用意します。

private MLRaycast.QueryParams _raycastParams = new MLRaycast.QueryParams();

あとは、RayのパラメータをUpdate関数内で更新します。
今回はコントローラーの向きに連動させています。

  //Rayの位置をコントローラーの向きに連動
  _raycastParams.Position = _controllerTransform.position;
  _raycastParams.Direction = _controllerTransform.forward;
  _raycastParams.UpVector = _controllerTransform.up;

  //変更したパラメーターとハンドラーをリクエストとして送る
  MLRaycast.Raycast(_raycastParams, HandleOnReceiveRaycast);

そして、第二引数の物体とRayが当たったときのコールバックが下記です。
引数として受け取った値を関数内で利用できます。
検出した平面の法線Rayが当たった場所などを取得して
利用できるので、とても便利です。

    /// <summary>
    /// コントローラーのレーザーポインターと平面が衝突したか検出
    /// </summary>
    /// <param name="state">レーザーの状態(当たり判定)</param>
    /// <param name="point">衝突した座標</param>
    /// <param name="normal">衝突した面の法線</param>
    /// <param name="confidence">面の認識率</param>
    private void HandleOnReceiveRaycast(MLRaycast.ResultState state, Vector3 point, Vector3 normal,float confidence)
    {
        //当たっていたらその場所が可視光線の終点
        if (state == MLRaycast.ResultState.HitObserved)
        {
            _laserLineRenderer.SetPosition(1,point);
            
            //オブジェクトの位置合わせ
            _pointerObject.transform.rotation = Quaternion.FromToRotation(Vector3.up, normal);
            _pointerObject.transform.position = point;
            
            Debug.Log("Hit");
        }
        //当たってなければ任意の長さの場所が終点
        else
        {
            _laserLineRenderer.SetPosition(1,_controllerTransform.forward * _laserDistance);
        }
    }

最後に

サンプルシーンが山盛りで、その他開発者へのサポートも充実しているので
MagicLeapは素晴らしいデバイスだと、開発者の個人的感想ですが思いました。



ギャップロを運営しているアップフロンティア株式会社では、一緒に働いてくれる仲間を随時、募集しています。 興味がある!一緒に働いてみたい!という方は下記よりご応募お待ちしております。
採用情報をみる