本文へスキップ

デバイスマップを使用して仮想コンテンツを配置する方法

注目してほしい!

この機能は実験的なものであり、期待通りに動作しない可能性があります。 詳しくは、デバイスマッピング機能ページをご覧ください。

デバイスマップを作成し、ファイルに保存したので、それを読み込んでARシーンに仮想オブジェクトを配置することができる。 このハウツーでは、デバイスマップファイルをUnityプロジェクトに読み込み、それを使ってコンテンツを配置する方法について説明します。

前提条件

始める前に、デバイス・マップの作成方法を完了してください。 このチュートリアルには、そのチュートリアルのUnityプロジェクトが必要です。 また、基本的な立方体など、バーチャルコンテンツとして配置するプレハブアセットが1つ必要です。

コンテンツを地図上に配置する

  1. HierarchyからDeviceMappingDemoを選択し、InspectorAdd Componentをクリックします。 新しいスクリプトを検索して追加し、名前をTracker.csとします。

  2. Tracker.cs`を開き、その内容を以下のスニペットで置き換える:

    using System.Collections;
    using System.IO;
    using Niantic.Lightship.AR.Mapping;
    using Niantic.Lightship.AR.PersistentAnchors;
    using UnityEngine;
    using UnityEngine.UI;
    using UnityEngine.XR.ARSubsystems;

    public class Tracker : MonoBehaviour
    {

    }
  3. Hierarchy** で XROrigin を選択し、InspectorAdd Component をクリックして ARPersistentAnchorManager を追加します。

  4. これで永続アンカー・マネージャーができたので、どのようなマッピングを使いたいかを指示できる。 Tracker`クラスに以下のスニペットを追加する:

    // initialize the manager
    [SerializeField]
    private ARPersistentAnchorManager _persistentAnchorManager;

    private void Start()
    {
    // tell it we want device mapping (aka "slick" mapping)
    _persistentAnchorManager.DeviceMappingLocalizationEnabled = true;
    _persistentAnchorManager.CloudLocalizationEnabled = false;
    // enable continuous localization to mitigate tracking drift
    _persistentAnchorManager.ContinuousLocalizationEnabled = true;
    _persistentAnchorManager.TemporalFusionEnabled = true;
    _persistentAnchorManager.TransformUpdateSmoothingEnabled = true;
    _persistentAnchorManager.DeviceMappingLocalizationRequestIntervalSeconds = 0.1f;

    // start the manager with our options
    StartCoroutine(_persistentAnchorManager.RestartSubsystemAsyncCoroutine());
    }
  5. Tracker`スクリプトにデバイスマップを選択するためのUI要素を追加:

    1. トラッキングを開始・停止するボタンを追加:
      1. 階層で右クリックし、Createメニューを開き、UIサブメニューからButtonを選択します。 これでCanvas**要素が作成され、そこにボタンが追加されます。
      2. スクリプトに、ボタン用のシリアライズされたフィールドを追加する。
    2. プライベートメソッド OnStartTrackingClicked() を作成し、Start() でボタンのクリックをリッスンするようにする。
    [SerializeField]
    private Button _startTrackingButton;

    private void Start()
    {
    // Add this below the previous section's code in Start()
    // Listen for button click event
    _startTrackingButton.onClick.AddListener(OnStartTrackingClicked);
    }

    // When tracking button is clicked
    private void OnStartTrackingClicked()
    {
    }

  6. Tracker.cs`に、ボタンがクリックされたときにトラッキングの状態を切り替えるロジックを追加する:

    private ARPersistentAnchor _anchor;
    // variable for reference to the object on the map
    private GameObject _anchorVisualObject;
    private bool _isTrackingRunning;

    // When tracking button is clicked
    private void OnStartTrackingClicked()
    {
    var buttonText = _startTrackingButton.GetComponentInChildren<Text>();
    if (_isTrackingRunning)
    {
    // Stop tracking if running and clean up anchor
    if (_anchor)
    {
    _persistentAnchorManager.DestroyAnchor(_anchor);
    _anchor = null;
    _anchorVisualObject = null;
    }
    buttonText.text = "Start Tracking";
    _isTrackingRunning = false;
    }
    // otherwise, change the button to "stop" and start tracking
    else
    {
    buttonText.text = "Stop Tracking";
    _isTrackingRunning = true;
    StartCoroutine(RestartTracking());
    }
    }
  7. 次のコード・スニペットを追加して、ファイルからマップを使用してトラッキングを再開します:

    1. ARDeviceMappingManager`のシリアライズフィールドを追加する。
    2. トラッキングを再開する前に、いくつかの要素をクリーンアップする必要がある:
      1. すでにアンカーがある場合は、それを破壊する。 状況によってはアンカーが破壊されるまでに1フレーム余分にかかることがあるので、続ける前に少し待つ。
      2. トラッキングを再開するには、ARPersistentAnchorManager を無効にして再度有効にする。 ロケーションデータとロケーションマネージャーデータが非同期にクリーンアップされるように、停止と開始の間にしばらく待つ。
    3. クリーンアップが完了したら、先ほど保存したファイルからシリアライズされたマップデータを読み込んで、ARDeviceMapを作成することができる。
      1. シリアライズされたマップデータをメモリに読み込み、新しい ARDeviceMap を作成して ARDeviceMappingManager.SetDeviceMap() に渡す。
    4. ARPersistentAnchorManager.TryTrackAnchor()`を呼び出し、新しいアンカーを使ってトラッキングを再開します。
  8. このスニペットを Tracker.cs に追加したら、Hierarchy を開き、DeviceMappingDemo を選択します。 次に、InspectorTracker.cs を見つけ、XROrigin から ARDeviceMappingManager をスクリプトのフィールドに割り当てます。

    [SerializeField]
    private ARDeviceMappingManager _deviceMappingManager;

    private IEnumerator RestartTracking()
    {
    if (_anchor)
    {
    _persistentAnchorManager.DestroyAnchor(_anchor);
    _anchor = null;
    _anchorVisualObject = null;
    }
    // wait a moment after destroying anchor
    yield return null;

    _persistentAnchorManager.enabled = false;

    // wait a moment before toggling tracking
    yield return null;

    _persistentAnchorManager.enabled = true;

    // Read a new device map from file
    var path = Path.Combine(Application.persistentDataPath, mapper.MapFileName);
    var serializedDeviceMap = File.ReadAllBytes(path);
    var deviceMap = ARDeviceMap.CreateFromSerializedData(serializedDeviceMap);

    // Assign the device map to the mapping manager
    _deviceMappingManager.SetDeviceMap(deviceMap);

    // Set up tracking with a new anchor
    _persistentAnchorManager.TryTrackAnchor(
    new ARPersistentAnchorPayload(deviceMap.GetAnchorPayload()),
    out _anchor);
    }
  9. アンカーが「トラッキング」状態になったら、デバイスマップに従って仮想オブジェクトを配置する:

    1. 仮想コンテンツを保持する変数を作成する:
      1. プレハブにシリアライズされたフィールド変数を追加し、それを自分のものに代入します。
    2. Start()`では、アンカー状態の変化をイベントリスナーに登録する。
    3. イベントの更新を処理する関数を作成する:
      1. アンカーがトラッキング状態にある場合、トラッキングされたアンカーを親とするオブジェクトをプレハブからインスタンス化します。
    [SerializeField]
    // replace yourPrefab with your prefab's name
    private GameObject _locationAnchorPrefab = yourPrefab;

    private void Start()
    {
    // Add this below the previous section's code in Start()
    // Listen for anchor updates and catch them
    _persistentAnchorManager.arPersistentAnchorStateChanged += OnArPersistentAnchorStateChanged;
    }

    // Event listener for anchor updates
    private void OnArPersistentAnchorStateChanged(ARPersistentAnchorStateChangedEventArgs args)
    {
    if (args.arPersistentAnchor.trackingState == TrackingState.Tracking)
    {
    if (!_anchorVisualObject)
    {
    _anchorVisualObject = Instantiate(_locationAnchorPrefab, _anchor.transform);
    }
    }
    }

これで仮想オブジェクトが表示されるはずです!

完成したトラッカー・スクリプト

トラッキング・スクリプトに問題がある場合は、ここで完成品と比較してみてください!

クリックしてTracker.csスクリプトを表示する。
using System.Collections;
using System.IO;
using Niantic.Lightship.AR.Mapping;
using Niantic.Lightship.AR.PersistentAnchors;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.XR.ARSubsystems;

public class Tracker : MonoBehaviour
{
// initialize the managers, prefab, menu, and map file I/O
[SerializeField]
private ARPersistentAnchorManager _persistentAnchorManager;

[SerializeField]
private ARDeviceMappingManager _deviceMappingManager;

[SerializeField]
// replace yourPrefab with your prefab's name
private GameObject _locationAnchorPrefab = yourPrefab;

[SerializeField]
private Button _startTrackingButton;

private GameObject _anchorVisualObject;
private ARPersistentAnchor _anchor;
private bool _isTrackingRunning;

private void Start()
{
// Event listeners for anchor and drop-down menu
_persistentAnchorManager.arPersistentAnchorStateChanged += OnArPersistentAnchorStateChanged;
_startTrackingButton.onClick.AddListener(OnStartTrackingClicked);

// tell it we want device mapping (aka "slick" mapping)
_persistentAnchorManager.DeviceMappingLocalizationEnabled = true;
_persistentAnchorManager.CloudLocalizationEnabled = false;
// enable continuous localization to mitigate tracking drift
_persistentAnchorManager.ContinuousLocalizationEnabled = true;
_persistentAnchorManager.TemporalFusionEnabled = true;
_persistentAnchorManager.TransformUpdateSmoothingEnabled = true;
_persistentAnchorManager.DeviceMappingLocalizationRequestIntervalSeconds = 0.1f;

// restart the manager with our options
StartCoroutine(_persistentAnchorManager.RestartSubsystemAsyncCoroutine());
}

// When tracking button is clicked
private void OnStartTrackingClicked()
{
var buttonText = _startTrackingButton.GetComponentInChildren<Text>();
if (_isTrackingRunning)
{
// Stop tracking if running
if (_anchor)
{
_persistentAnchorManager.DestroyAnchor(_anchor);
_anchor = null;
_anchorVisualObject = null;
}
buttonText.text = "Start Tracking";
_isTrackingRunning = false;
}
else
{
buttonText.text = "Stop Tracking";
_isTrackingRunning = true;
StartCoroutine(RestartTracking());
}
}

private IEnumerator RestartTracking()
{
if (_anchor)
{
_persistentAnchorManager.DestroyAnchor(_anchor);
_anchor = null;
_anchorVisualObject = null;
}
yield return null;

_persistentAnchorManager.enabled = false;

// start tracking after stop tracking needs "some" time in between...
yield return null;

_persistentAnchorManager.enabled = true;

// Read a new device map from file
var path = Path.Combine(Application.persistentDataPath, mapper.MapFileName);
var serializedDeviceMap = File.ReadAllBytes(path);
var deviceMap = ARDeviceMap.CreateFromSerializedData(serializedDeviceMap);

// Assign the device map to the mapping manager
_deviceMappingManager.SetDeviceMap(deviceMap);

// Set up tracking with a new anchor
_persistentAnchorManager.TryTrackAnchor(
new ARPersistentAnchorPayload(deviceMap.GetAnchorPayload()),
out _anchor);
}

// Event listener for anchor updates
private void OnArPersistentAnchorStateChanged(ARPersistentAnchorStateChangedEventArgs args)
{
if (args.arPersistentAnchor.trackingState == TrackingState.Tracking)
{
if (!_anchorVisualObject)
{
_anchorVisualObject = Instantiate(_locationAnchorPrefab, _anchor.transform);
Debug.Log("Tracking");
}
}
}
}