Developing for Lightship on the Magic Leap 2
Working with Lightship ARDK on the Magic Leap 2 is, in many ways, the same as developing an AR experience for mobile devices. Due to hardware differences between the Magic Leap 2 and a standard smartphone, some tasks must be done differently. In this How-To, you will learn about the differences and how to address them in your Unity project.
Prerequisites
- You must complete the Magic Leap 2 Lightship setup process in the other PDF before using this How-To.
- You will need a valid Lightship API key attached to your Unity project for the VPS section of this How-To. See the Setup page for more information.
- Scripts in this How-To use elements from the
ARLocationManager
and VPS Coverage APIs. If you need a refresher, see How to Use Location AR with Code and Querying VPS Coverage for AR Locations at Runtime.
Playback on the Magic Leap 2
By playing through pre-recorded footage in the Unity editor, Lightship's Playback system is able to simulate AR applications on the Magic Leap 2 in the same way as it does on mobile devices.
To simulate your AR experience on the Magic Leap 2:
- Before creating your Playback recording, add the
Lightship ML Rig
prefab included with the Magic Leap 2 package to your AR scene. This prefab includes components that help the editor's input provider send data to the binaries used to run the computer vision features. - Follow the instructions in Using the API to Record Datasets to record your playback dataset. Note: You must record in landscape to use the recording on the Magic Leap 2.
- In Unity, open the Lightship top menu, then select Lightship Settings. In the Playback section, enter the path to your recording in the Dataset Path field.
- Set up your XR scene with any necessary scripts and components, then press Play in the Editor to run the Unity scene using your recording.
Spoofing Locations for the Magic Leap 2
VPS no longer requires GPS coordinates when localizing, but the Coverage API still requires them. Because the Magic Leap 2 does not have GPS hardware, this means you will need to spoof a location to use some of Lightship's location-based features. ARDK contains a class, LightshipLocationSpoof
, that can be initialized as a static instance when starting location services on the device and used to pass coordinate information to Coverage API features at runtime.
You must still accept location service permissions on the Magic Leap 2 when spoofing a location, even though GPS is not available on the device.
To spoof a location using ARDK:
- In Unity, open the Lightship top menu, then check the Enabled box under Spoof Location.
- At the top of your script, include
Niantic.Lightship.AR.Input
to make sure location data is stored properly:
// Add at the top of your script
using Input = Niantic.Lightship.AR.Input;
- In your main script, create a
LightshipLocationSpoof
object, then use its fields to set the GPS coordinates you want to spoof. (Make sure the coordinates are typed as floats!)
// Setting location coordinates
LightshipLocationSpoof.Instance.Latitude = 37.795668f;
LightshipLocationSpoof.Instance.Longitude = -122.393429f;
- At any point after this in your script, you can verify your coordinates using the
Input
system and create variables to easily pass the values around:
// Location verification using the Input system
var latitude = Input.location.lastData.latitude;
var longitude = Input.location.lastData.longitude;
Once your GPS coordinates are set and verified, you should be able to use them to query locations in the VPS Coverage API. These values can be changed at any time, so queries can be made quickly across different locations.
Example Spoofing Script
This example script walks through the process of spoofing GPS coordinates and getting them ready to pass to other parts of your code. It takes in locations from an ARLocationManager
, provides a way to select which location you want to spoof, then passes the location information through the Input
system to get it ready for the Magic Leap 2.
Click to reveal the location spoofing demo script
// 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}";
}
}
}