Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9747

MagicLeapで床にオブジェクトを配置する方法

$
0
0

開発環境

Unity : 2019.3.7f1
LuminOS : 0.98.11, APILevel 8
MagicLeap : UnitySDK 0.24.1
MagicLeap : ToolKit 特にバージョン表記等はないので現時点(2020/09/22)での最新

MagicLeapToolKitのDLはこちらから

今回開発したアプリのリポジトリはこちらになります

完成するもの

下準備

ProjectSettings > MagicLeap > ManifestSettingsにて以下の項目にチェックを入れました

  • ControllerPose
  • LowLatencyLightwear
  • WorldReconstruction

Manifest.png

スクリプト等

今回のスクリプトはMagicLeap ToolKitのPlaceOnFloorを改造したものです

PlaceOnFloor.png

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

改造したFloorChecker.cs

usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;#if PLATFORM_LUMIN
usingUnityEngine.XR.MagicLeap;#endif
namespaceFloorCheck{/// <summary>/// MagicLeapToolsのFloorOnPlaceを改造したクラス./// 床検知を何度もにできるようにする./// </summary>publicclassFloorChecker:MonoBehaviour{readonlyfloatHeadLocationIdleThreshold=0.003f;readonlyfloatHeadRotationIdleThreshold=.3f;readonlyintHistoryCount=5;readonlyfloatHeadIdleRequiredDuration=.2f;// Public Properties:publicVector3Location{get;privateset;}[Tooltip("Does content's content match it's transform forward?")][SerializeField]boolflippedForward;List<Vector3>headLocationHistory;List<Quaternion>headRotationHistory;floatheadLocationVelocity;floatheadRotationVelocity;TransformmainCamera;boolheadLocationIdle;boolheadRotationIdle;boolheadTemporarilyIdle;boolheadIdle;boolplacementValid;//Init:voidAwake(){//refs:mainCamera=Camera.main.transform;//requirements:if(FindObjectOfType<MLSpatialMapper>()==null){Debug.LogError("PlaceOnFloor requires and instance of the MLSpatialMapper in your scene.");}}//Flow:voidOnEnable(){//sets:headLocationHistory=newList<Vector3>();headRotationHistory=newList<Quaternion>();}//Loops:voidUpdate(){//let headpose warmup a little:if(Time.frameCount<3){return;}HeadActivityDetermination();}//Coroutines:IEnumeratorHeadIdleTimeout(){yieldreturnnewWaitForSeconds(HeadIdleRequiredDuration);headIdle=true;}voidHeadActivityDetermination(){//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(inti=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(inti=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(Rayray){//cast to see if we are looking at the floor:RaycastHithit;if(Physics.Raycast(ray,outhit)){MagicLeapTools.SurfaceTypesurface=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

usingSystem;usingMagicLeapTools;usingUnityEngine;namespaceFloorCheck{/// <summary>/// トリガを入力したときに床を判定し、床の場合はオブジェクトを配置するサンプル./// </summary>[RequireComponent(typeof(FloorChecker),typeof(AudioSource))]publicclassFloorCheckOnPlaceContent:MonoBehaviour{[SerializeField]AudioClippressClip;[SerializeField]AudioClipsuccessClip;[SerializeField]AudioClipfailedClip;[SerializeField]GameObjectcontent;[SerializeField]Pointerpointer;FloorCheckerfloorChecker;AudioSourceaudio;voidStart(){floorChecker=GetComponent<FloorChecker>();audio=GetComponent<AudioSource>();}publicvoidOnTriggerDown(){audio.PlayOneShot(pressClip);(boolonFloor,Vector3pos)result=floorChecker.LookingAtFloorDetermination(newRay(pointer.Origin,pointer.Direction));if(result.onFloor){audio.PlayOneShot(successClip);content.transform.position=result.pos;}else{audio.PlayOneShot(failedClip);}}}}

シーンの構成

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

Scene.png


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

MainCamera.png


ControlPointerは Assets > MagicLeap-Tools > Prefabs > Inputから

ControllerPointer.png


今回はSpatialMapperを表示してどのメッシュの判定が通っているかをわかりやすくするので Assets > MagicLeap > Core > Assets > PrefabsのMLSpatialMapperも利用します

MLSpatialMapper.png

MLSpatialMapperにはメッシュを生成するルートとなるオブジェクトが必要なのでシーン上にMeshRootオブジェクトを作成しそれをあてがっています

SpatialMapper.png


FloorCheckerを利用するクラス等はこのような構成になります
効果音は魔王魂さんから拝借

FloorChecker.png

トリガ入力に対応して床判定を行うためにControlPointerのイベントにFloorCheckOnPlaceContentのOnTriggerDownを登録しています

Attack.png

完成

実機にビルド or ZeroIterationで動作確認をすれば

これで床を判定し、床だけに配置したいオブジェクトとかの実装ができるようになります

感想

この判定を使えば床判定の入ってるメッシュだけAgentのNavMeshを晴れたりできるかも?

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


Viewing all articles
Browse latest Browse all 9747

Trending Articles