稀疏空間地圖的對應用環境的要求和平面圖像識別可以比照理解,周圍環境需要足夠豐富,不能有大片的單色區域,透明區域。此外,光照,角度都會對建立地圖和定位產生影響。
官方給出了建立地圖和定位地圖的建議。https://help.easyar.cn/EasyAR%20Sense/v4/Guides/EasyAR-Sparse-Spatial-Map.html
總體說明
稀疏空間地圖的基礎是運動跟蹤,所有在場景種首先要有運動跟蹤的全套遊戲對象包括設置。然後主要的是 SparseSpatialMapWorker 和 SparseSpatialMap 這兩個遊戲對象。
SparseSpatialMapWorker 遊戲對象相關
- Locailzation Mode 屬性在建立地圖的時候通常選“UntilSuccess”,在定位的時候,通常選“KeepUpdate”。
- Use Global Service Config 選項可以設置是否使用全局定義的稀疏空間地圖信息。
- BuilderMapController.Host(…)方法是保存地圖的方法,需要輸入的參數是地圖的名稱和地圖的縮略圖,縮略圖可以輸入“null”。
- BuilderMapController.MapHost 事件用於返回地圖保存情況的事件。事件有 3 個參數,是地圖保存成功後的名稱,ID,是否保存成功的狀態,還有錯誤信息。
- Localizer.startLocalization()和 Localizer.stopLocalization()方法是用來啓動和停止本地稀疏空間定位的方法。如果 SparseSpatialMap 遊戲對象設置了地圖的 ID 和名稱的時候,默認會自動啓動地圖定位。
SparseSpatialMap 遊戲對象相關
SparseSpatialMap 遊戲對象是稀疏空間地圖在 Unity 中的載體,每個稀疏空間地圖在定位的時候對應一個 SparseSpatialMap 遊戲對象,同一個場景可以同時有多個稀疏空間地圖。希望在某個稀疏空間地圖中放置的虛擬物體,將其對應的遊戲對象放置到對應的 SparseSpatialMap 遊戲對象下成爲其子游戲對象即可。
- Source Type 屬性用於設置稀疏空間地圖的作用,即是用於建立地圖“Map Builder”還是用於定位“Map Manager”。
- Map Worker 屬性必須關聯對應的 SparseSpatialMapWorker 遊戲對象。通常不需要設置。
- Show Point Cloud 選項可以設置是否點雲的效果。在建圖的時候,顯示點雲的效果能幫助使用者更好的建立稀疏空間地圖。
- MapLoad 事件是指定的稀疏空間地圖從服務器端下載到本地觸發的事件。
- MapLocalized、MapStopLocalize 事件是地圖實現定位和停止定位的事件。MapLocalized 可以被觸發多次,或者理解爲可以不斷修正位置。
建立地圖
- 設置場景中的 Main Camera 的 Clear Flags 屬性爲 Solid Color。
- 將 EasyAR/Prefabs/Composites 目錄下的 EasyAR_SparseSpatialMapWorker 預製件拖到場景中。
- 將 EasyAR/Prefabs/Primitives 目錄下的 WorldRoot 預製件拖到場景中。
- 選中 EasyAR_ SparseSpatialMapWorker 遊戲對象,將 WorldRoot 遊戲對象拖到 World Root Controller 屬性中爲其賦值。
- 將 EasyAR/Prefabs/Primitives 目錄下的 SparseSpatialMap 預製件拖到場景中。
在 SparseSpatialMap 遊戲對象下再添加用於分辨方向的模型,用形狀來和 WorldRoot 遊戲對象下的進行區分。
添加界面和腳本。界面中有保存按鈕和顯示反饋信息的文本框。
腳本說明:
保存完地圖需要將獲取的ID和Name保存下來。官方沒有提供稀疏空間地圖訪問的API。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using easyar;
using System;
public class BuildMapController : MonoBehaviour
{
//稀疏空間地圖相關對象
private ARSession session;
private SparseSpatialMapWorkerFrameFilter mapWorker;
private SparseSpatialMapController map;
/// <summary>
/// 保存按鈕
/// </summary>
private Button btnSave;
/// <summary>
/// 顯示文本
/// </summary>
private Text text;
void Start()
{
//稀疏空間地圖初始
session = FindObjectOfType<ARSession>();
mapWorker = FindObjectOfType<SparseSpatialMapWorkerFrameFilter>();
map = FindObjectOfType<SparseSpatialMapController>();
//註冊追蹤狀態變化事件
session.WorldRootController.TrackingStatusChanged += OnTrackingStatusChanged;
//初始化保存按鈕
btnSave = GameObject.Find("/Canvas/Button").GetComponent<Button>();
btnSave.onClick.AddListener(Save);
btnSave.interactable = false;
if (session.WorldRootController.TrackingStatus == MotionTrackingStatus.Tracking)
{
btnSave.interactable = true;
}
else
{
btnSave.interactable = false;
}
//初始化顯示文本
text = GameObject.Find("/Canvas/Panel/Text").GetComponent<Text>();
}
/// <summary>
/// 保存地圖方法
/// </summary>
private void Save()
{
btnSave.interactable = false;
//註冊地圖保存結果反饋事件
mapWorker.BuilderMapController.MapHost += SaveMapHostBack;
//保存地圖
try
{
//保存地圖
mapWorker.BuilderMapController.Host("LearnMap" + DateTime.Now.ToString("yyyyMMddHHmm"), null);
text.text = "開始保存地圖,請稍等。";
}
catch (Exception ex)
{
btnSave.interactable = true;
text.text = "保存出錯:" + ex.Message;
}
}
/// <summary>
/// 保存地圖反饋
/// </summary>
/// <param name="mapInfo">地圖信息</param>
/// <param name="isSuccess">成功標識</param>
/// <param name="error">錯誤信息</param>
private void SaveMapHostBack(SparseSpatialMapController.SparseSpatialMapInfo mapInfo, bool isSuccess, string error)
{
if (isSuccess)
{
PlayerPrefs.SetString("MapID", mapInfo.ID);
PlayerPrefs.SetString("MapName", mapInfo.Name);
text.text = "地圖保存成功。\r\nMapID:" + mapInfo.ID + "\r\nMapName:" + mapInfo.Name;
}
else
{
btnSave.interactable = true;
text.text = "地圖保存出錯:" + error;
}
}
/// <summary>
/// 攝像機狀態變化
/// </summary>
/// <param name="status">狀態</param>
private void OnTrackingStatusChanged(MotionTrackingStatus status)
{
if (status == MotionTrackingStatus.Tracking)
{
btnSave.interactable = true;
text.text = "進入跟蹤狀態。";
}
else
{
btnSave.interactable = false;
text.text = "退出跟蹤狀態。" + status.ToString();
}
}
}
打包以後在設備上運行。進入以後,會顯示點雲效果。稀疏空間地圖SparseSpatialMap遊戲對象和運動跟蹤WorldRoot遊戲對象兩者的原點方向都是一致的。當掃描完周圍空間以後,點擊保存按鈕,就可以將地圖保存到服務器。在提示文本中會顯示地圖的ID和名稱。
本地化地圖
- 將建立地圖場景另存爲作爲本地化的場景。
- 選中SparseSpatialMapWorker遊戲對象,修改Localization Mode屬性爲Keep Update。
- 選中SparseSpatialMap遊戲對象,修改Source Type屬性爲Map Manager。
修改界面,設置界面裏有2個輸入框,分別用於輸入地圖ID和地圖名稱,有一個文本框顯示提示內容,2個按鈕,用於本地化地圖和停止定位。
替換腳本。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;
using easyar;
public class LocalizeMapController : MonoBehaviour
{
//稀疏空間地圖相關對象
private ARSession session;
private SparseSpatialMapWorkerFrameFilter mapWorker;
private SparseSpatialMapController map;
/// <summary>
///地圖 ID輸入框
/// </summary>
public InputField inputID;
/// <summary>
/// 地圖名稱輸入框
/// </summary>
public InputField inputName;
/// <summary>
/// 文本顯示
/// </summary>
public Text text;
void Start()
{
//稀疏空間地圖初始
session = FindObjectOfType<ARSession>();
mapWorker = FindObjectOfType<SparseSpatialMapWorkerFrameFilter>();
map = FindObjectOfType<SparseSpatialMapController>();
//如果之前有建立過地圖且文本框沒有預設值
if (inputID.text.Length <= 0)
{
inputID.text = PlayerPrefs.GetString("MapID", "");
inputName.text = PlayerPrefs.GetString("MapName", "");
}
map.MapLoad += MapLoadBack; //註冊地圖加載事件
map.MapLocalized += LocalizedMap; //註冊定位成功事件
map.MapStopLocalize += StopLocalizeMap; //註冊停止定位事件
StartLocalization();
}
/// <summary>
/// 地圖加載反饋
/// </summary>
/// <param name="mapInfo">地圖信息</param>
/// <param name="isSuccess">是否成功</param>
/// <param name="error">錯誤信息</param>
private void MapLoadBack(SparseSpatialMapController.SparseSpatialMapInfo mapInfo, bool isSuccess, string error)
{
if (isSuccess)
{
text.text = "地圖" + mapInfo.Name + "加載成功。";
}
else
{
text.text = "地圖加載失敗。" + error;
}
}
/// <summary>
/// 地圖定位成功
/// </summary>
private void LocalizedMap()
{
text.text = "稀疏空間地圖定位成功。" + DateTime.Now.ToShortTimeString();
}
/// <summary>
/// 停止地圖定位
/// </summary>
private void StopLocalizeMap()
{
text.text = "稀疏空間地圖停止定位。" + DateTime.Now.ToShortTimeString();
}
/// <summary>
/// 開始本地化地圖
/// </summary>
public void StartLocalization()
{
//文本框內容不爲空
if (inputID.text.Length > 0 && inputName.text.Length > 0)
{
map.MapManagerSource.ID = inputID.text;
map.MapManagerSource.Name = inputName.text;
}
text.text = "開始本地化地圖。";
mapWorker.Localizer.startLocalization();
}
/// <summary>
/// 停止本地化
/// </summary>
public void StopLocalization()
{
mapWorker.Localizer.stopLocalization();
}
}
打包後在設備上運行,默認獲取之前建立的地圖。期間可以停止定位以後,輸入新的地圖ID和名稱再點擊Start開始本地化地圖。
地圖的原點和方向即SparseSpatialMap遊戲對象的位置方向和本地化時候設備的狀態無關,和建立地圖時候的狀態一致。