MagicLeap에서 오브젝트 제크트를 실행하는 방법

28440 단어 magicleapunitycsharp

開発環境



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

MagicLeapToolKitのDLはこちらから

今回開発したアプリの リポジトリはこちら
FloorCheck신에산프루가配置してあります

完成するもの

床を判定するやつ pic.twitter.com/zzhb4lk5g2 — 松本隆介(
)

下準備


ProjectSettings > MagicLeap > ManifestSettings
にて以下の項目にcheckkを入れました
  • 컨트롤러 포즈
  • LowLatencyLightwear
  • WorldReconstruction


  • 스크립트 등



    今回의 스크립팅은 MagicLeap ToolKit의 PlaceOnFloor를 통해 가능합니다.



    素のPlaceOnFloorのままだ와 初回の床判定以降は床判定を行わないので何度でも床判定を行えるようにしました.

    다음은 FloorChecker.cs 파일입니다.

    
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    #if PLATFORM_LUMIN
    using UnityEngine.XR.MagicLeap;
    #endif
    
    
    namespace FloorCheck
    {
        /// <summary>
        /// MagicLeapToolsのFloorOnPlaceを改造したクラス.
        /// 床検知を何度もにできるようにする.
        /// </summary>
        public class FloorChecker : MonoBehaviour
        {
            readonly float HeadLocationIdleThreshold = 0.003f;
            readonly float HeadRotationIdleThreshold = .3f;
            readonly int HistoryCount = 5;
            readonly float HeadIdleRequiredDuration = .2f;
    
            // Public Properties:
            public Vector3 Location
            {
                get;
                private set;
            }
    
    
            [Tooltip("Does content's content match it's transform forward?")]
            [SerializeField] bool flippedForward;
    
    
            List<Vector3> headLocationHistory;
            List<Quaternion> headRotationHistory;
            float headLocationVelocity;
            float headRotationVelocity;
            Transform mainCamera;
            bool headLocationIdle;
            bool headRotationIdle;
            bool headTemporarilyIdle;
            bool headIdle;
            bool placementValid;
    
    
            //Init:
            void Awake()
            {
                //refs:
                mainCamera = Camera.main.transform;
    
                //requirements:
                if (FindObjectOfType<MLSpatialMapper>() == null)
                {
                    Debug.LogError("PlaceOnFloor requires and instance of the MLSpatialMapper in your scene.");
                }
            }
    
    
            //Flow:
            void OnEnable()
            {
                //sets:
                headLocationHistory = new List<Vector3>();
                headRotationHistory = new List<Quaternion>();
            }
    
    
            //Loops:
            void Update()
            {
                //let headpose warmup a little:
                if (Time.frameCount < 3)
                {
                    return;
                }
    
                HeadActivityDetermination(); 
            }
    
    
            //Coroutines:
            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>
            /// <returns></returns>
            public (bool, Vector3) LookingAtFloorDetermination(
                Ray ray)
            {
                //cast to see if we are looking at the floor:
                RaycastHit hit;
                if (Physics.Raycast(ray, out hit))
                {
                    MagicLeapTools.SurfaceType surface = MagicLeapTools.SurfaceDetails.Analyze(hit);
    
                    if (surface == MagicLeapTools.SurfaceType.Floor)
                    {
                        Location = hit.point;
                        placementValid = true;
                        return (true, Location);
                    }
                    else
                    {
                        placementValid = false;
                        return (false, Vector3.zero);
                    }
                }
                else
                {
                    placementValid = false;
                    return (false, Vector3.zero);
                }
            }
    
    
        }
    }
    



    FloorChecker를 사용할 수 있는FloorCheckOnPlaceContent.cs

    using System;
    using MagicLeapTools;
    using UnityEngine;
    
    namespace FloorCheck
    {
    
        /// <summary>
        /// トリガを入力したときに床を判定し、床の場合はオブジェクトを配置するサンプル.
        /// </summary>
        [RequireComponent(typeof(FloorChecker),typeof(AudioSource))]
        public class FloorCheckOnPlaceContent : MonoBehaviour
        {
    
            [SerializeField] AudioClip pressClip;
            [SerializeField] AudioClip successClip;
            [SerializeField] AudioClip failedClip;
            [SerializeField] GameObject content;
            [SerializeField] Pointer pointer;
            FloorChecker floorChecker;
            AudioSource audio;
    
    
            void Start()
            {
                floorChecker = GetComponent<FloorChecker>();
                audio = GetComponent<AudioSource>();
            }
    
    
            public void OnTriggerDown()
            {
                audio.PlayOneShot(pressClip);
                (bool onFloor, Vector3 pos ) result = floorChecker.LookingAtFloorDetermination(new Ray(pointer.Origin, pointer.Direction));
                if (result.onFloor)
                {
                    audio.PlayOneShot(successClip);
                    content.transform.position = result.pos;
                }
                else
                {
                    audio.PlayOneShot(failedClip);
                }
            }
    
    
    
        }
    }
    

    shin の 構成



    シーンの構成は以下の画像の通りになっています




    MainCameraは

    Assets > MagicLeap > Core > Assets > Prefabs
    にある物を使いました



    ControlPointerは

    Assets > MagicLeap-Tools > Prefabs > Input
    (으)로부터




    今回はSpatialMapperを表示してどのメッシュの判定が通っているかをわかりやすくするので

    Assets > MagicLeap > Core > Assets > Prefabs
    のMLSpatialMapper도 사용 가능



    MLSpatialMapper는 메쉬 생성을 생성하는 루트가 될 수 있는 개체 생성이 가능합니다. MeshRoot 개체 생성을 생성할 수 있습니다.




    FloorChecker(을)를 사용하는 클래스는 다음과 같이 구성할 수 있습니다.
    効果音은 魔王魂さんから拝借



    Triga入力に対応して床判定を行うためにControlPointer의 이벤트에FloorCheckOnPlaceContent의OnTriggerDown을 실행합니다



    完成



    実機にBird 또는 ZeroIterationで動作確認をすれば

    床を判定するやつ pic.twitter.com/zzhb4lk5g2 — 松本隆介(
    )


    これで床を判定し、床だけに配置したいOBJECTTOKANO 実装ができるようになります

    感想



    이 정의를 사용하여 에이전트의 NavMesh를 사용하거나 제거하거나 できるかも?

    まだやってない、出来たら記事にするかもしれません

    좋은 웹페이지 즐겨찾기