Skip to main content

How to Create a Device Map

Attention!

This feature is experimental and may not work as expected. For more information about it, see the Device Mapping Feature page.

Before you can use a device map in your AR application, you will need to create and save it. In this tutorial, we will go over how to create and save a device map in your Unity project, as well as some tips for how to get the most out of device mapping.

Prerequisites

You will need a Unity project with ARDK installed and a basic AR scene. For more information, see Setting Up Lightship ARDK and Setting up a Basic AR Scene.

Creating a Device Map

To create and store a device map:

  1. In the Hierarchy, select the XROrigin, then, in the Inspector, click Add Component and add an ARDeviceMappingManager.
  2. In the Hierarchy, right-click the root of your AR scene, then select Create Empty. Name the new object DeviceMappingDemo.
  3. Select DeviceMappingDemo from the Hierarchy, then, in the Inspector, click Add Component. Search for and add a New Script to it, then name it Mapper.cs.
  4. Open Mapper.cs and replace its contents with the following snippet:
using System;
using System.Collections;
using System.IO;
using Niantic.Lightship.AR.Mapping;
using UnityEngine;
using UnityEngine.UI;

public class Mapper : MonoBehaviour
{

}
  1. Add UI elements for the mapper:

    1. Right-click in the Hierarchy, then open the Create menu and select Button from the UI sub-menu. This will create a Canvas element and add the button to it.
  2. Add this snippet to the Mapper class to map for ten seconds when the button is pressed:

    [SerializeField]
    private ARDeviceMappingManager _deviceMappingManager;

    [SerializeField]
    private Button _startMappingButton;

    private void Start()
    {
    _startMappingButton.onClick.AddListener(OnStartMappingClicked);
    }

    // Set this function to be called when button is clicked
    public void OnStartMappingClicked()
    {
    StartCoroutine(RunMapping());
    }

    private IEnumerator RunMapping()
    {
    // disable the button after clicking so that only one map is made at a time
    _startMappingButton.gameObject.SetActive(false);
    _deviceMappingManager.SetDeviceMap(new ARDeviceMap());
    _deviceMappingManager.StartMapping();
    // change this value to map for more or less time
    yield return new WaitForSeconds(10.0f);
    _deviceMappingManager.StopMapping();
    _startMappingButton.gameObject.SetActive(true);
    }
note

Ten seconds is enough time to map a small area, such as a desk or couch. If you are mapping a larger area, increase the mapping time to compensate.

  1. After the UI code, add an event listener for ARDeviceMappingManager.DeviceMapFinalized so that we know when to save the map:

    private const string MapFileNamePrefix = "SerializedDeviceMapData";

    private void Start()
    {
    _deviceMappingManager.DeviceMapFinalized += OnDeviceMapFinalized;
    }

    private void OnDeviceMapFinalized(ARDeviceMap map)
    {
    if (map.HasValidMap())
    {
    // final map generated. save to the file system
    var serializedDeviceMap = map.Serialize();
    var path = Path.Combine(Application.persistentDataPath, MapFileName);
    File.WriteAllBytes(path, serializedDeviceMap);
    Debug.Log($"Map saved");
    }
    else
    {
    Debug.LogError("Map was empty");
    }
    }
note

A final device map will be generated after StopMapping() is called, but it will not be available until the DeviceMapFinalized event is invoked. Make sure to wait for the finalized event after the button is pressed!

Optional: Show the Mesh While Mapping

By default, ARDeviceMappingManager does not provide any visual feedback while mapping an area. By adding meshing to your project, you can visualize your map's coverage and get a sense of where you still need to cover. The mesh cannot exactly show where the device map was generated or its localizability, but it can provide a general idea of the map's area. To learn about how to add meshing to your Lightship project, see Creating a Mesh.

Device Mapping Tips

  • Because device mapping relies on a single scan, localization and tracking accuracy depend heavily on how well you scan the area. If you remember one tip, let it be this: if you are having localization issues, don't be afraid to re-scan!
  • Lighting matters a lot when scanning an area. If the lighting during localization doesn't match the scan, you will need to re-scan. (This includes both outdoor and indoor lighting!)
  • For best results, follow these guidelines when scanning:
    • Focus on distinctive objects in the area that are unlikely to move between scanning and localization, such as furniture, household fixtures, or statues.
    • Avoid looking primarily at flat surfaces without color variation, such as the ground, grass, walls, and floors.
    • Move around while scanning! The more viewpoints you see an object from, the better the map of it will be.

Complete Device Mapping Script

If you are having trouble with your mapping script, compare it to the finished product here!

Click here to reveal the final Mapper.cs script
using System;
using System.Collections;
using System.IO;
using Niantic.Lightship.AR.Mapping;
using UnityEngine;
using UnityEngine.UI;

public class Mapper : MonoBehaviour
{
public const string MapFileName = "SerializedDeviceMapData";

[SerializeField]
private ARDeviceMappingManager _deviceMappingManager;

[SerializeField]
private Button _startMappingButton;

private void Start()
{
_deviceMappingManager.DeviceMapFinalized += OnDeviceMapFinalized;
_startMappingButton.onClick.AddListener(OnStartMappingClicked);
}

private void OnDeviceMapFinalized(ARDeviceMap map)
{
if (map.HasValidMap())
{
// final map generated. save to the file system
var serializedDeviceMap = map.Serialize();
var path = Path.Combine(Application.persistentDataPath, MapFileName);
File.WriteAllBytes(path, serializedDeviceMap);
Debug.Log($"Map saved");
}
else
{
Debug.LogError("Map was empty");
}
}

// Set this function to be called when button is clicked
public void OnStartMappingClicked()
{
StartCoroutine(RunMapping());
}

private IEnumerator RunMapping()
{
_startMappingButton.gameObject.SetActive(false);
_deviceMappingManager.SetDeviceMap(new ARDeviceMap());
_deviceMappingManager.StartMapping();
yield return new WaitForSeconds(10.0f);
_deviceMappingManager.StopMapping();
_startMappingButton.gameObject.SetActive(true);
}

}

Next Steps

Once you have created and saved a device map, move on to How to Place Real-World Content Using a Device Map.