MagicLeap에서 의미의 평면에 오브제크트를 실행하는 방법

33226 단어 magicleapunitycsharp
基本的な開発環境やshinの構成は同じ

開発環境



유니티 : 2019.3.7f1
LuminOS : 0.98.11, API레벨 8
MagicLeap : UnitySDK 0.24.1
MagicLeap : ToolKit 특별 버전 表記等はないので現時点(2020/09/23)での最新

MagicLeapToolKitのDLはこちらから

今回開発したアプリの リポジトリはこちら
PlaneCheck신에 산프루가 配置하고 있습니다

完成するもの



任意の平面を判定するやつ pic.twitter.com/bJ8k1Udb5k — 松本隆介(
)


下準備



この辺りは前回の記事と同様なので飛ばしても大丈夫です
ProjectSettings > MagicLeap > ManifestSettings
にて以下の項目にcheckkを入れました
  • 컨트롤러 포즈
  • LowLatencyLightwear
  • WorldReconstruction



  • 신노체성





    基本的には前回とはあmari変わってませんがどの平面を判定しているかを確認するためRuntimeConsoleをnewたに追加しました

    RuntimeConsoleは

    MagicLeap-Tools > Prefabs > Debugging
    にあるPrehabをshin上に配置しています

    PlaneChecker오브제크트에 첨부할 수 있는 스크립트로 사진을 찍을 수 있습니다.


    스크립트



    前回作成したFloorCheckerをさらに改造したPlaneCheckerを作成します

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    #if PLATFORM_LUMIN
    using UnityEngine.XR.MagicLeap;
    #endif
    
    
    namespace PlaneCheck
    {
        /// <summary>
        /// MagicLeapToolsのFloorOnPlaceを改造したクラス.
        /// 任意の平面を判定する.
        /// </summary>
        public class PlaneChecker : MonoBehaviour
        {
            readonly float HeadLocationIdleThreshold = 0.003f;
            readonly float HeadRotationIdleThreshold = .3f;
            readonly int HistoryCount = 5;
            readonly float HeadIdleRequiredDuration = .2f;
    
    
            public Vector3 Location
            {
                get;
                private set;
            }
    
            List<Vector3> headLocationHistory;
            List<Quaternion> headRotationHistory;
            float headLocationVelocity;
            float headRotationVelocity;
            Transform mainCamera;
            bool headLocationIdle;
            bool headRotationIdle;
            bool headTemporarilyIdle;
            bool headIdle;
            bool placementValid;
    
    
            void Awake()
            {
                mainCamera = Camera.main.transform;
    
                if (FindObjectOfType<MLSpatialMapper>() == null)
                {
                    Debug.LogError("PlaceOnFloor requires and instance of the MLSpatialMapper in your scene.");
                }
            }
    
    
            void OnEnable()
            {
                headLocationHistory = new List<Vector3>();
                headRotationHistory = new List<Quaternion>();
            }
    
    
            void Update()
            {
                if (Time.frameCount < 3)
                {
                    return;
                }
    
                HeadActivityDetermination(); 
            }
    
    
            IEnumerator HeadIdleTimeout()
            {
                yield return new WaitForSeconds(HeadIdleRequiredDuration);
                headIdle = true;
            }
    
    
            void HeadActivityDetermination()
            {
                //history:
                headLocationHistory.Add(mainCamera.position);
                if (HistoryCount < headLocationHistory.Count)
                    headLocationHistory.RemoveAt(0);
    
                headRotationHistory.Add(mainCamera.rotation);
                if (HistoryCount < headRotationHistory.Count)
                    headRotationHistory.RemoveAt(0);
    
                //location velocity:
                if (headLocationHistory.Count == HistoryCount)
                {
                    headLocationVelocity = 0;
                    for (int i = 1; i < headLocationHistory.Count; i++)
                    {
                        headLocationVelocity += Vector3.Distance(headLocationHistory[i], headLocationHistory[i - 1]);
                    }
                    headLocationVelocity /= headLocationHistory.Count;
    
                    //idle detection:
                    if (headLocationVelocity <= HeadLocationIdleThreshold)
                    {
                        if (!headLocationIdle)
                        {
                            headLocationIdle = true;
                        }
                    }
                    else
                    {
                        if (headLocationIdle)
                        {
                            headLocationIdle = false;
                        }
                    }
                }
    
                //rotation velocity:
                if (headRotationHistory.Count == HistoryCount)
                {
                    headRotationVelocity = 0;
                    for (int i = 1; i < headRotationHistory.Count; i++)
                    {
                        headRotationVelocity += Quaternion.Angle(headRotationHistory[i], headRotationHistory[i - 1]);
                    }
                    headRotationVelocity /= headRotationHistory.Count;
    
                    //idle detection:
                    if (headRotationVelocity <= HeadRotationIdleThreshold)
                    {
                        if (!headRotationIdle)
                        {
                            headRotationIdle = true;
                        }
                    }
                    else
                    {
                        if (headRotationIdle)
                        {
                            headRotationIdle = false;
                        }
                    }
                }
    
                //absolute idle head determination:
                if (headLocationIdle && headRotationIdle)
                {
                    if (!headTemporarilyIdle)
                    {
                        headTemporarilyIdle = true;
                        StartCoroutine(HeadIdleTimeout());
                    }
                }
                else
                {
                    if (headTemporarilyIdle)
                    {
                        headIdle = false;
                        headTemporarilyIdle = false;
                        StopCoroutine(HeadIdleTimeout());
                    }
                }
            }
    
    
            /// <summary>
            /// 指定したRayの位置に任意の面があるか否か、ある場合はその座標も返す.
            /// </summary>
            /// <param name="ray"></param>
            /// <param name="surfaceType"></param>
            /// <returns></returns>
            public (bool, Vector3) LookingAtFloorDetermination(
                Ray ray,
                MagicLeapTools.SurfaceType surfaceType)
            {
                RaycastHit hit;
                if (Physics.Raycast(ray, out hit))
                {
                    MagicLeapTools.SurfaceType surface = MagicLeapTools.SurfaceDetails.Analyze(hit);
    
                    if (surface == surfaceType)
                    {
                        Location = hit.point;
                        placementValid = true;
                        return (true, Location);
                    }
                    else
                    {
                        placementValid = false;
                        return (false, Vector3.zero);
                    }
                }
                else
                {
                    placementValid = false;
                    return (false, Vector3.zero);
                }
            }
    
    
        }
    }
    



    この部分で任意の平面かの判定をとっています



            /// <summary>
            /// 指定したRayの位置に任意の面があるか否か、ある場合はその座標も返す.
            /// </summary>
            /// <param name="ray"></param>
            /// <param name="surfaceType"></param>
            /// <returns></returns>
            public (bool, Vector3) LookingAtFloorDetermination(
                Ray ray,
                MagicLeapTools.SurfaceType surfaceType)
            {
                RaycastHit hit;
                if (Physics.Raycast(ray, out hit))
                {
                    MagicLeapTools.SurfaceType surface = MagicLeapTools.SurfaceDetails.Analyze(hit);
    
                    if (surface == surfaceType)
                    {
                        Location = hit.point;
                        placementValid = true;
                        return (true, Location);
                    }
                    else
                    {
                        placementValid = false;
                        return (false, Vector3.zero);
                    }
                }
                else
                {
                    placementValid = false;
                    return (false, Vector3.zero);
                }
            }
    
    

    判定する ことが出来る平面はMagicLeapTools의SurfaceDetils.csに定義されています
    今回の三種類を判定する ことにしました


    PlaneChecker를 사용하려면 PlaneCheckOnPlaceContent.cs



    基本的な構成は前回のFloorCheckOnPlaceContent.cs와 あまり変わっていません
    범퍼보탄을 押下したら判定を切り替えるようにしています
    ControlPointer오브제크트의 ControlInputにOnBumperButtonDown()を登録


    using MagicLeapTools;
    using UnityEngine;
    
    namespace PlaneCheck
    {
    
        /// <summary>
        /// トリガを入力したときに任意の平面を判定し、床の場合はオブジェクトを配置するサンプル.
        /// </summary>
        [RequireComponent(typeof(PlaneChecker),typeof(AudioSource))]
        public class PlaneCheckOnPlaceContent : MonoBehaviour
        {
    
            [SerializeField] AudioClip pressClip;
            [SerializeField] AudioClip successClip;
            [SerializeField] AudioClip failedClip;
            [SerializeField] GameObject content;
            [SerializeField] Pointer pointer;
            [SerializeField] RuntimeConsole runtimeConsole;
            PlaneChecker planeChecker;
            AudioSource audio;
            SurfaceType[] surfaceTypes;
            int index = 0;
    
            void Start()
            {
                planeChecker = GetComponent<PlaneChecker>();
                audio = GetComponent<AudioSource>();
    
                // RuntimeConsoleに自分が指定した文字列だけ表示したい.
                runtimeConsole.errors = false;
                runtimeConsole.logs = false;
                runtimeConsole.warnings = false;
    
                // 今回はこの三種類のみをチェック.
                surfaceTypes = new[]
                {
                    SurfaceType.Floor,   // 床.
                    SurfaceType.Wall,    // 壁.
                    SurfaceType.Ceiling, // 天井.
                };
            }
    
    
            public void OnBumperButtonDown()
            {
                index = (int)Mathf.Repeat(index + 1, surfaceTypes.Length);
                runtimeConsole.logText.text = surfaceTypes[index].ToString();
            }
    
    
            public void OnTriggerDown()
            {
                audio.PlayOneShot(pressClip);
                (bool onSurfaceType, Vector3 pos ) result = planeChecker.LookingAtFloorDetermination(new Ray(pointer.Origin, pointer.Direction), surfaceTypes[index]);
                if (result.onSurfaceType)
                {
                    audio.PlayOneShot(successClip);
                    content.transform.position = result.pos;
                }
                else
                {
                    audio.PlayOneShot(failedClip);
                }
            }
    
    
    
        }
    }
    



    完成



    これでデプロイ or ZeorIterationで確認すれば↓の様な動作になります
    ただしRuntimeConsoleは実機にデプロイ時にしか動作しないので実機での確認のほうがどの平面を判定しているかがわかりやすいです

    任意の平面を判定するやつ pic.twitter.com/bJ8k1Udb5k — 松本隆介(
    )


    感想



    これで任意の平面にOBJECQUETOを配置したりできるようになりました
    なんか家具の配置確認アプリ的なもので天井にランプをつるすとか、壁に絵を飾るとかの確認に使うのがmein所の使い方かな?

    좋은 웹페이지 즐겨찾기