本文へスキップ

Lightshipを使用してMagic Leap 2上で開発する

Lightship ARDKを使用してMagic Leap 2上で開発することは、モバイルデバイス向けのAR体験を開発する場合と多くの点で同じです。 しかし、Magic Leap 2と一般的なスマートフォンではハードウェアが異なるため、一部の作業は異なる方法で行う必要があります。 この入門ガイドでは、Unityプロジェクトにおける違いと、その対処法について学びます。

前提条件

  1. この入門ガイドを使用する前に、別のPDFに記載されているMagic Leap 2のLightship設定プロセスを完了しておく必要があります。
  2. この入門ガイドのVPSセクションを進めるには、有効なLightship APIキーをUnityプロジェクトにアタッチしておく必要があります。 詳しくは、Lightship ARDKを設定するをご覧ください。
  3. この入門ガイドにあるスクリプトでは、 ARLocationManager とVPS Coverage APIの要素を使用します。 再確認が必要な場合は、ロケーションARとコードを一緒に使用する and Querying VPS Coverage for AR Locations at Runtimeを参照してください。

Magic Leap 2でのプレイバック

Unityエディターで事前に録画した映像を再生すれば、Lightshipのプレイバックシステムを使用して、モバイルデバイスと同様にMagic Leap 2上でARアプリケーションをシミュレートできます。

Magic Leap 2上でAR体験をシミュレートするには、以下の手順を行います。

  1. プレイバック録画を作成する前に、Magic Leap 2のパッケージに含まれている Lightship ML Rig プレハブをARシーンに追加します。 このプレハブには、エディターの入力プロバイダーから、コンピュータービジョン機能を実行するためのバイナリにデータを送信するコンポーネントが含まれています。
  2. プレイバックデータセットを録画するには、APIを使ってデータセットを記録するの手順に従ってください。 注: Magic Leap 2で録画を使用するには、必ず landscape(ランドスケープモード) で録画してください。
  3. Unityで、上部の Lightship メニューを開き、 Lightship Settings を選択します。 Playback セクションで、 Dataset Path フィールドに録画データのパスを入力します。
  4. 必要なスクリプトとコンポーネントを指定してXRシーンを設定したら、エディターで[プレイ]を押し、録画を使用してUnityシーンを実行します。

Magic Leap 2を使用して位置情報を偽装する

VPSのローカライゼーションではGPS座標が不要になりましたが、Coverage APIでは依然として必要です。 Magic Leap 2にはGPSハードウェアが搭載されていないため、Lightshipで利用する位置情報機能によっては位置情報を偽装する必要があります。 ARDKには LightshipLocationSpoof というクラスがあり、デバイスで位置情報サービスを開始する際に静的インスタンスとして初期化し、ランタイム時に座標情報をCoverage API機能に渡すことができます。

注意

GPS機能がデバイスに搭載されていなくても、Magic Leap 2で位置情報を偽装する際は、位置情報サービスの利用を許可する必要があります。

ARDKを使って位置情報を偽装するには、次の手順を行います。

  1. Unityで、上部の Lightship メニューを開き、 Spoof LocationEnabled ボックスにチェックを入れます。
  2. 位置データが正しく保存されるように、スクリプトの先頭に Niantic.Lightship.AR.Input を追加します。
// Add at the top of your script
using Input = Niantic.Lightship.AR.Input;
  1. メインスクリプト内で LightshipLocationSpoof オブジェクトを作成したら、そのフィールドを使って、位置を偽装したいGPS座標を設定します (座標は必ず浮動小数点(float型)で入力してください)。
// Setting location coordinates
LightshipLocationSpoof.Instance.Latitude = 37.795668f;
LightshipLocationSpoof.Instance.Longitude = -122.393429f;
  1. これで、スクリプト内の任意の時点で Input システムを使って座標を確認し、値を渡しやすいように変数を作成することができます。
// Location verification using the Input system
var latitude = Input.location.lastData.latitude;
var longitude = Input.location.lastData.longitude;

GPS座標を設定し、確認したら、それを使ってVPS Coverage APIでロケーションに対してクエリを実行できるようになります。 これらの値はいつでも変更できるため、異なるロケーション間で素早くクエリを実行することができます。

位置偽装スクリプトの例

このサンプルスクリプトでは、GPS座標を偽装するプロセスについて解説します。 ARLocationManager から位置情報を取得し、偽装したいロケーションを選択する方法を確認してから、 Input システムを使用して、Magic Leap 2用にその位置情報を準備していきます。

クリックして、位置偽装のデモスクリプトを表示
// Copyright 2022-2024 Niantic.
using System.Collections.Generic;
using Niantic.Lightship.AR;
using Niantic.Lightship.AR.Loader;
using Niantic.Lightship.AR.LocationAR;
using Niantic.Lightship.AR.PersistentAnchors;
using Niantic.Lightship.AR.VpsCoverage;
using UnityEngine;
using UnityEngine.UI;

using Input = Niantic.Lightship.AR.Input;

namespace Niantic.Lightship.MagicLeap.InternalSamples
{
public class VPSLocalizationSample : MonoBehaviour
{
[SerializeField, Tooltip("The Location Manager")]
private ARLocationManager _arLocationManager;

[Header("UI")]
[SerializeField, Tooltip("The Dropdown for Persistent AR Locations")]
private Dropdown _arLocationDropdown;

[SerializeField, Tooltip("The Button to select an AR Location")]
private Button _arLocationChooseButton;

[SerializeField, Tooltip("The UI Canvas to display the AR Location Selector")]
private GameObject _arLocationUI;

[SerializeField, Tooltip("Text display for latitude and longitude")]
private Text _gpsText;

private List<ARLocation> _arLocationsDropdownItems = new List<ARLocation>();

private void OnEnable()
{
if (!LightshipSettings.Instance.UseLightshipSpoofLocation)
{
Debug.LogError
(
"Magic Leap does not provide GPS, which is necessary for Lightship VPS. " +
"Please enable the Spoof Location feature in the Lightship Settings menu in " +
"order to try out the VPS Localization Sample."
);

return;
}

_arLocationManager.locationTrackingStateChanged += OnLocationTrackingStateChanged;

if (_arLocationManager.AutoTrack)
{
_arLocationUI.SetActive(false);
}
else
{
CreateARLocationMenu();
_arLocationUI.SetActive(true);
_arLocationChooseButton.onClick.AddListener(OnLocationSelected);
}

if (Input.location.status == LocationServiceStatus.Stopped)
{
Input.location.Start();
}

UpdateGPSText();
}

private void OnDisable()
{
_arLocationManager.locationTrackingStateChanged -= OnLocationTrackingStateChanged;
_arLocationChooseButton.onClick.RemoveListener(OnLocationSelected);
}

private void CreateARLocationMenu()
{
var arLocations = _arLocationManager.ARLocations;
foreach (var arLocation in arLocations)
{
_arLocationDropdown.options.Add(new Dropdown.OptionData(arLocation.name));
_arLocationsDropdownItems.Add(arLocation);
}

if (_arLocationsDropdownItems.Count > 0)
{
_arLocationChooseButton.interactable = true;
}
}

private void OnLocationSelected()
{
var currentIndex = _arLocationDropdown.value;
var arLocation = _arLocationsDropdownItems[currentIndex];
_arLocationManager.SetARLocations(arLocation);

var gpsLocation = arLocation.GpsLocation;
LightshipLocationSpoof.Instance.Latitude = (float)gpsLocation.Latitude;
LightshipLocationSpoof.Instance.Longitude = (float)gpsLocation.Longitude;
UpdateGPSText();

_arLocationManager.StartTracking();
}

private void OnLocationTrackingStateChanged(ARLocationTrackedEventArgs args)
{
if (!args.Tracking)
{
// We de-activate the gameObject when we lose tracking.
// ARLocationManager will not de-activate it automatically
args.ARLocation.gameObject.SetActive(false);
}
}

private void UpdateGPSText()
{
// First, check if user has location service enabled
if (!Input.location.isEnabledByUser)
{
_gpsText.text = "Location service is not enabled";
return;
}

var latitude = Input.location.lastData.latitude;
var longitude = Input.location.lastData.longitude;
var altitude = Input.location.lastData.altitude;

_gpsText.text = $"Latitude: {latitude}\nLongitude: {longitude}\nAltitude: {altitude}";
}
}
}