こんにちは、情報システム部の高野です。
今回は、オブジェクトの移動を試してみます。
前回作ったプロジェクトをそのまま利用します。
HoloLensでオブジェクト移動するために色々と調べてみたところ
いくつかの実装方法があることが分かりました。
今回は、そのうちの2つの方法を試してみます。
1つは、UnityのInteractionManagerのイベントを使う方法
もう1つは、HoloToolkitのIManipulationHandlerを実装する方法です。
InteractionManagerを利用した移動
前回、オブジェクトを生成できるようにしたので
その生成したオブジェクトを移動できるようにしていきます。
移動用のスクリプトを作成する
ProjectウィンドウのScriptsフォルダにGestureControllerというスクリプトを作成します。
作成してGestureControllerをHierarchyウィンドウのObjectManagerに
ドラッグ&ドロップし設定します。
CubeプレハブにTagを設定する
後程、別オブジェクトを作成するので区別できるようにTagを設定しておきます。
ProjectウィンドウのPrefabsフォルダからCubeを選択します。
Inspectorウィンドウの上部にTagプルダウンがあるので「Add Tag」を選択します。
TagsにあるプラスボタンをクリックしTag名をInteractionとし保存します。
もう一度CubeプレハブのInspectorウィンドウを開き
Tagで「Interaction」を選択します。
Tagに設定した状態
移動処理を実装する
GestureControllerスクリプトをVisualStudioで開きます。
コードを以下の様に変更します
using HoloToolkit.Unity.InputModule; using UnityEngine; using UnityEngine.VR.WSA.Input; public class GestureController : MonoBehaviour { private Vector3 prevPos; private bool isHold; private GameObject focusObj; // Use this for initialization void Start() { InteractionManager.SourcePressed += InteractionManager_SourcePressed; InteractionManager.SourceReleased += InteractionManager_SourceReleased; InteractionManager.SourceLost += InteractionManager_SourceLost; InteractionManager.SourceUpdated += InteractionManager_SourceUpdated; } // Update is called once per frame void Update() { var obj = GazeManager.Instance.HitObject; // ホールドしている時は、オブジェクト入れ替えない if (obj != null && !isHold) { // TagがInteractionのものだけを対象とする if (obj.tag == "Interaction") { focusObj = obj; } } } private void InteractionManager_SourcePressed(InteractionSourceState state) { if (focusObj == null) return; focusObj.GetComponent<Rigidbody>().useGravity = false; Vector3 handPosition; if (state.source.kind == InteractionSourceKind.Hand && state.properties.location.TryGetPosition(out handPosition)) { isHold = true; prevPos = handPosition; } } private void InteractionManager_SourceReleased(InteractionSourceState state) { if (focusObj == null) return; focusObj.GetComponent<Rigidbody>().useGravity = true; isHold = false; focusObj = null; } private void InteractionManager_SourceLost(InteractionSourceState state) { if (focusObj == null) return; focusObj.GetComponent<Rigidbody>().useGravity = true; isHold = false; focusObj = null; } private void InteractionManager_SourceUpdated(InteractionSourceState state) { if (!isHold || focusObj == null) return; Vector3 handPosition; state.properties.location.TryGetPosition(out handPosition); if (state.source.kind == InteractionSourceKind.Hand && state.properties.location.TryGetPosition(out handPosition)) { var moveVector = Vector3.zero; moveVector = handPosition - prevPos; prevPos = handPosition; var handDistance = Vector3.Distance(Camera.main.transform.position, handPosition); var objectDistance = Vector3.Distance(Camera.main.transform.position, focusObj.transform.position); focusObj.transform.position += (moveVector * (objectDistance / handDistance)); } } }
InteractionManager_SourcePressedで手の位置を取得しておき
InteractionManager_SourceUpdatedで移動距離を計算して
オブジェクトに新しい位置を割り当てます。
この移動方法は下記の記事を参考にさせていただきました。
qiita.com
もうちょっと複雑な計算をしているものも見かけたのですが
今のところは、この方法で支障がないような気がしています。
IManipulationHandlerを利用した移動
今度はHoloToolkitのIManipulationHandlerインターフェイスを実装する方法です。
HoloToolkitを利用しているので、できればこちらの方法で実装した方が良いかと思います。
新たなCubeプレハブを作成する
元々、用意してあったCubeとは別のオブジェクトを生成して
そちらをIManipulationHandlerで移動するようにしていきます。
前回、作成したようにCubeのプレハブを作成します。
見た目で判別できるように黄色にしておきます。
プレハブ名もYellowCubeにします。
RigidbodyとCubeManagerスクリプトも設定しておきます。
YelloCubeを生成できるようにコードを変更する
前回作成したObejectManagerスクリプトのコードを変更します。
using HoloToolkit.Unity.InputModule; using UnityEngine; public class ObjectManager : MonoBehaviour, IInputClickHandler { public GameObject obj1; public GameObject obj2; private bool toggle = true; private void Start() { InputManager.Instance.PushFallbackInputHandler(gameObject); } public void OnInputClicked(InputClickedEventData eventData) { var obj = toggle ? obj1 : obj2; toggle = !toggle; var pos = Camera.main.transform.position; var forword = Camera.main.transform.forward; Instantiate(obj, pos + forword, new Quaternion()); } }
Unityに戻りobj1にCubeプレハブをobj2にYellowCubeプレハブを設定します。
移動処理を実装する
Unityで新しいスクリプト、ManipulationControllerを作成します。
作成したスクリプトをYellowCubeプレハブに設定します。
設定したスクリプトをVisualStudioで開き
下記の様にコードを変更します。
using HoloToolkit.Unity.InputModule; using UnityEngine; public class ManipulationController : MonoBehaviour, IManipulationHandler { Vector3 prevPos; public void OnManipulationCanceled(ManipulationEventData eventData) { GetComponent<Rigidbody>().useGravity = true; InputManager.Instance.PopModalInputHandler(); } public void OnManipulationCompleted(ManipulationEventData eventData) { GetComponent<Rigidbody>().useGravity = true; InputManager.Instance.PopModalInputHandler(); } public void OnManipulationStarted(ManipulationEventData eventData) { GetComponent<Rigidbody>().useGravity = false; prevPos = eventData.CumulativeDelta; // これが無いとオブジェクトにフォーカス時しか操作ができない InputManager.Instance.PushModalInputHandler(gameObject); } public void OnManipulationUpdated(ManipulationEventData eventData) { Vector3 moveVector = Vector3.zero; moveVector = eventData.CumulativeDelta - prevPos; prevPos = eventData.CumulativeDelta; // 手の位置が取得できないので決め打ちで40cmに var handDistance = 0.4f; var objectDistance = Vector3.Distance(Camera.main.transform.position, gameObject.transform.position); gameObject.transform.position += (moveVector * (objectDistance / handDistance)); } }
移動のロジックは、InteractionManagerの方とあまり変わりはありませんが
IManipulationHandlerだと元記事にもありますが手の位置の取得方法が不明です。
私もいろいろと試しましたが今のところ分かっていません。
下記を実行するとfalseが返ってくるので取得できる望みは低いのはないかと思います。
eventData.InputSource.SupportsInputInfo(eventData.SourceId, SupportedInputInfo.Position)
OnManipulationUpdatedメソッドは、InteractionManager.SourceUpdatedイベントと違い
フォーカスが外れるとイベントが飛びません。
それを回避するためにInputManager.Instance.PushModalInputHandlerメソッドに
gameObjectを渡しています。
今回、作成したものはこんな感じで動きます。
HoloLensでオブジェクトを移動する
動画では分かりづらいのですが、
緑のオブジェクト(InteractionManagerを使った方)の方が
手の移動とオブジェクトの移動がしっくりきます。
HoloToolkitを利用してオブジェクトを移動するなら
IManipulationHandlerを実装する方法でやりたいところですが
手の位置の取得方法が分からないのでうーんって感じです。
(やり方分かる方は、こそっと教えてほしいです)
今回は以上になります。次もHoloLensネタになるかと思います。
弊社ではエンジニアを募集しています。
興味がある方は下記からエントリお願いします。
athome-inc.jp