MRが楽しい

MRやVRについて学習したことを書き残す

SharingWithUNETについてまとめる その7

本日は HoloToolKit の調査枠です。
SharingWithUNET のサンプルシーンを確認していきます。
bluebirdofoz.hatenablog.com

今回は HologramCollection オブジェクトにある SharedCollection について機能を確認します。
f:id:bluebirdofoz:20180112005351j:plain

SharedCollection は本オブジェクト自体には何の機能もありません。
アタッチされているスクリプトは以下の通り空クラスです。

・SharedCollection.cs

    /// <summary>
    /// This script exists as a stub to allow other scripts to find 
    /// the shared world anchor transform.
    /// このスクリプトはスタブとして存在し、他のスクリプトが共有世界のアンカー変換を見つけることを可能にします。
    /// </summary>
    public class SharedCollection : SingleInstance<SharedCollection>
    {

    }

しかし、本オブジェクトは各ホストの共通座標を示すという重要な役割を担います。
本オブジェクトは SharingWithUNET のシーン内で以下のように、検出することができます。
・UNetAnchorManager.cs

        private bool CheckConfiguration()
        {
(中略)
            // SharedCollectionのインスタンスを取得する。
            if (SharedCollection.Instance == null)
            {
                // SharedCollectionのインスタンスを取得できない場合、エラー
                Debug.Log("No SharedCollection found in scene");
                return false;
            }

#if UNITY_WSA
            // SharedCollectionのインスタンスを持つゲームオブジェクトを取得する。
            objectToAnchor = SharedCollection.Instance.gameObject;
(中略)
#endif

            return true;
        }


サーバ側は以下の UNetAnchorManager の ExportAnchorAtPosition 関数で、HologramCollection オブジェクトにアンカーを設定します。
・UNetAnchorManager.cs

        /// <summary>
        /// Createx and exports the anchor at the specified world position
        /// Createxは、指定されたワールド位置にアンカーをエクスポートします。
        /// </summary>
        /// <param name="worldPos">The position to place the anchor(アンカーを配置する位置)</param>
        private void ExportAnchorAtPosition(Vector3 worldPos)
        {
            // Need to remove any anchor that is on the object before we can move the object.
            // オブジェクトを移動する前に、オブジェクト上にあるアンカーを削除する必要があります。
            // SharedCollectionのインスタンスを持つゲームオブジェクトに設定されたアンカーを取得する。
            WorldAnchor worldAnchor = objectToAnchor.GetComponent<WorldAnchor>();
            if (worldAnchor != null)
            {
                // 既存のワールドアンカーが存在する場合

                // 既存のワールドアンカーを破棄する。
                DestroyImmediate(worldAnchor);
            }

            // Move the object to the specifid place
            // SharedCollectionのインスタンスを持つゲームオブジェクトを指定された場所に移動する。
            objectToAnchor.transform.position = worldPos;

            // Attach a new anchor
            // 新しいアンカーをアタッチする。
            worldAnchor = objectToAnchor.AddComponent<WorldAnchor>();

            // Name the anchor
            // アンカーに名前を付ける。
            exportingAnchorName = Guid.NewGuid().ToString();
            Debug.Log("preparing " + exportingAnchorName);

            // Register for on tracking changed in case the anchor isn't already located
            // アンカーがまだ位置していない場合に変更されたトラッキングのために
            // イベントとしてWorldAnchor_OnTrackingChangedを登録する。
            worldAnchor.OnTrackingChanged += WorldAnchor_OnTrackingChanged;

            // And call our callback in line just in case it is already located.
            // すでにコールバックされている場合に備えて、コールバックをオンラインで呼び出すことができます。
            WorldAnchor_OnTrackingChanged(worldAnchor, worldAnchor.isLocated);
        }


ホスト側は以下の UNetAnchorManager の ImportComplete 関数で、HologramCollection オブジェクトに受信したアンカーを設定します。
・UNetAnchorManager.cs

        private void ImportComplete(SerializationCompletionReason status, WorldAnchorTransferBatch wat)
        {
            // インポートが成功したかどうかをチェックする。
            if (status == SerializationCompletionReason.Succeeded && wat.GetAllIds().Length > 0)
            {
                // インポートが成功し、かつアンカーが1つ以上の場合
                Debug.Log("Import complete");

                // 1つ目のアンカー名を取得する。
                string first = wat.GetAllIds()[0];
                Debug.Log("Anchor name: " + first);

                // SharedCollectionのインスタンスを持つゲームオブジェクトに設定されたWorldAnchorを取得する。
                WorldAnchor existingAnchor = objectToAnchor.GetComponent<WorldAnchor>();
                if (existingAnchor != null)
                {
                    // WorldAnchorが存在した場合

                    // 既存のWorldAnchorを削除する
                    DestroyImmediate(existingAnchor);
                }

                // SharedCollectionのインスタンスを持つゲームオブジェクトにインポートしたアンカーを設定する。
                WorldAnchor anchor = wat.LockObject(first, objectToAnchor);
                // オブジェクトのトラッキング状態が変化したときのイベントにAnchor_OnTrackingChangedを設定する。
                anchor.OnTrackingChanged += Anchor_OnTrackingChanged;
                // ワールドアンカーが存在する場合は即座にWorldAnchorManagerに記録する。
                Anchor_OnTrackingChanged(anchor, anchor.isLocated);

                // インポート実行中フラグを落とす。
                ImportInProgress = false;
            }
            else
            {
                // if we failed, we can simply try again.
                // 失敗した場合は、もう一度やり直すことができます。
                // 更新実行フラグを立てる。
                gotOne = true;
                Debug.Log("Import fail");
            }
        }


Player.prefabなど他のオブジェクトはこの HologramCollection オブジェクトを検出することで共通の座標にオブジェクトを配置できます。
・PlayerController.cs

        /// <summary>
        /// 起動時処理
        /// </summary>
        private void Start()
        {
            // シェアリング空間のインスタンスがnullのとき、このプレイヤーインスタンスを破棄する。
            if (SharedCollection.Instance == null)
            {
                Debug.LogError("This script required a SharedCollection script attached to a gameobject in the scene");
                Destroy(this);
                return;
            }
(中略)
            // シェアリング空間のTransform値を設定する。
            sharedWorldAnchorTransform = SharedCollection.Instance.gameObject.transform;
            // 自身のオブジェクトの親Transformをシェアリング空間のTransform値と同じにする。
            transform.SetParent(sharedWorldAnchorTransform);
        }


また本オブジェクトは子オブジェクトとして、アンカーデバッグ用のUIオブジェクトを持ちます。
f:id:bluebirdofoz:20180112005330j:plain
SharingWithUNET のシーン内で本UIの表示位置が現在のアンカー位置になります。
シェアリングが上手くいかないときは、これで全てのホストでアンカー位置が同じか確認するとよいです。