【Unity-学习-014】EasyAR4.0稀疏空间地图 扫描场景功能

本帖主要描写扫描场景的功能实现,以及一些需要注意的问题。跟上层贴有所关联,想要更多了解请移步链接

场景中有几个重要的预设需要添加。目录如下:

其中:

SparseSpatialMap用于扫描空间成成点云信息,点云可以将空间数据以点的信息保存下来SparseSpatialMapController
WorldRoot点云的空间位置的基本参照WorldRootController
EasyAR_SparseSpatialMapWorker点云保存上传等一些操作的入口ARSession/SparseSpatialMapWorkerFrameFilter

 

场景中,我用Text来debug一些信息,可以显示当前的进度。

操作流程:进入场景开始扫描云点,然后点击保存之后弹出窗口,开始编辑地图名字,编辑完成后,点击上传。

然后创建SceneMaster来对场景进行管理。添加脚本BuildMapController.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using easyar;
using System;
using SpatialMap_SparseSpatialMap;
using UnityEngine.SceneManagement;

public class BuildMapController : MonoBehaviour
{
    //稀疏空间地图相关对象
    private ARSession session;
    private SparseSpatialMapWorkerFrameFilter mapWorker;
    private SparseSpatialMapController map;
    /// <summary>
    /// 保存按钮
    /// </summary>
    private Button btnSave; //保存按钮
    private Button btnUpload;  //输入名字后上传
    private GameObject SavePanel;
    /// <summary>
    /// 显示文本
    /// </summary>
    private Text text;

    private InputField inputField;


    private void Awake()
    {
        //稀疏空间地图初始
        session = FindObjectOfType<ARSession>();
        mapWorker = FindObjectOfType<SparseSpatialMapWorkerFrameFilter>();
        map = FindObjectOfType<SparseSpatialMapController>();
        SavePanel = GameObject.Find("MapName");
        inputField = SavePanel.transform.Find("Panel/InputField").GetComponent<InputField>();
        btnUpload = SavePanel.transform.Find("Panel/Btn_Upload").GetComponent<Button>();
        btnSave = GameObject.Find("Canvas/Map/Btn_SaveMap").GetComponent<Button>();
        //显示文本
        text = GameObject.Find("Canvas/Map/Text").GetComponent<Text>();
    }

    void Start()
    {
        map.MapWorker = mapWorker;
        //注册追踪状态变化事件
        session.WorldRootController.TrackingStatusChanged += OnTrackingStatusChanged;

        SavePanel.SetActive(false);
        //初始化保存按钮
        btnSave.onClick.AddListener(OpenUploadPanel);
        btnUpload.onClick.AddListener(Save);
        btnSave.interactable = false;
        if (session.WorldRootController.TrackingStatus == MotionTrackingStatus.Tracking)
        {
            btnSave.interactable = true;
        }
        else
        {
            btnSave.interactable = false;
        }
    }

    /// <summary>
    /// 保存地图方法
    /// </summary>
    private void OpenUploadPanel()
    {
        btnSave.interactable = false;
        SavePanel.SetActive(true);
    }

    string mapName = "";
    private void Save()
    {
        //注册地图保存结果反馈事件
        mapWorker.BuilderMapController.MapHost += SaveMapHostBack;
        //mapWorker.BuilderMapController.MapLoad += LoadMapHostBack;
        //保存地图
        try
        {
            if (!inputField.text.Equals(string.Empty))
            {
                mapName = "Map" + inputField.text;
            }
            else
            {
                mapName = "Map" + DateTime.Now.ToString("YYYY-MM-DD-HH-mm-ss");
            }
            //保存地图
            mapWorker.BuilderMapController.Host(mapName, 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)
        {
            SavePanel.SetActive(false);

            PlayerPrefs.SetString("MapID", mapInfo.ID);
            PlayerPrefs.SetString("MapName", mapInfo.Name);
            text.text = "地图保存成功。\r\nMapID:" + mapInfo.ID + "\r\nMapName:" + mapInfo.Name;
            MapMetaManager.Save(new MapMeta(mapInfo, new List<MapMeta.PropInfo>()), MapMetaManager.FileNameType.Name);
            Invoke("BackMain", 3);
        }
        else
        {
            btnSave.interactable = true;
            text.text = "地图保存出错:" + error;
        }
    }

    public void BackMain()
    {
        SceneManager.LoadScene("MainMapScene");
    }




    /// <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();
        }
    }
}




代码相关:

在Awake获取到三个主要的脚本,然后在start中完成初始化。

1.将 mapWorker 填充进 mapController 中的MapWorker属性中。这一步也可以在场景中直接拖拽也行。

2.在session.WorldRootController.TrackingStatusChanged 注册事件。

 ///注册追踪状态变化事件
 session.WorldRootController.TrackingStatusChanged += OnTrackingStatusChanged;

这个事件的用处是可以监听当前的追踪状态。拿到追踪状态就可做一些事情。例如我用来提示,也可以用来操作一些游戏对象的状态等,或者弹出UI提醒用户做一些操作等。

 private void OnTrackingStatusChanged(MotionTrackingStatus status)
    {
        if (status == MotionTrackingStatus.Tracking)
        {
            btnSave.interactable = true;
            text.text = "进入跟踪状态。";
        }
        else
        {
            btnSave.interactable = false;
            text.text = "退出跟踪状态。" + status.ToString();
        }
    }

3.扫描场景。扫描的过程中,随着点云数量的增加,手机会变的逐渐卡顿,最大的场景支持100平方米,其实也不大,就10*10的地块。实际使用下来一般50平方左右的空间最舒服。

4.点击保存按钮弹出框框,编辑名字后点击上传。

 string mapName = "";
    private void Save()
    {
        //注册地图保存结果反馈事件
        mapWorker.BuilderMapController.MapHost += SaveMapHostBack;
        //mapWorker.BuilderMapController.MapLoad += LoadMapHostBack;
        //保存地图
        try
        {
            if (!inputField.text.Equals(string.Empty))
            {
                mapName = "Map" + inputField.text;
            }
            else
            {
                mapName = "Map" + DateTime.Now.ToString("YYYY-MM-DD-HH-mm-ss");
            }
            //保存地图
            mapWorker.BuilderMapController.Host(mapName, null);
            text.text = "开始保存地图,请稍等。";
        }
        catch (Exception ex)
        {
            btnSave.interactable = true;
            text.text = "保存出错:" + ex.Message;
        }
    }

首先注册一个保存地图后的回调。该回调在完成上传后调用,不管成功与否。

 mapWorker.BuilderMapController.MapHost += SaveMapHostBack;

然后 调用 host 方法来上传地图。

  //保存地图
  mapWorker.BuilderMapController.Host(mapName, null);

然后是对回调信息的处理。处理很简单,就是将这个地图的ID和名字用 PlayerPrefs 存到本地,这个东西自己的觉得方便就用了。大家可以用自己喜欢的方法Json或者其他本地化的方法实现。

    /// <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)
        {
            SavePanel.SetActive(false);

            PlayerPrefs.SetString("MapID", mapInfo.ID);
            PlayerPrefs.SetString("MapName", mapInfo.Name);
            text.text = "地图保存成功。\r\nMapID:" + mapInfo.ID + "\r\nMapName:" + mapInfo.Name;
            MapMetaManager.Save(new MapMeta(mapInfo, new List<MapMeta.PropInfo>()), MapMetaManager.FileNameType.Name);
            Invoke("BackMain", 3);
        }
        else
        {
            btnSave.interactable = true;
            text.text = "地图保存出错:" + error;
        }
    }

然后是 MapMetaManager 这个脚本。这个脚本用来处理点云信息。将meta 信息存储到本地先,用来下一步场景编辑时使用。

//================================================================================================================================
//
//  Copyright (c) 2015-2020 VisionStar Information Technology (Shanghai) Co., Ltd. All Rights Reserved.
//  EasyAR is the registered trademark or trademark of VisionStar Information Technology (Shanghai) Co., Ltd in China
//  and other countries for the augmented reality technology developed by VisionStar Information Technology (Shanghai) Co., Ltd.
//
//================================================================================================================================

using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json; 
using UnityEngine;
namespace SpatialMap_SparseSpatialMap
{
    public class MapMetaManager
    {
        public enum FileNameType
        {
            ID, Name
        }

        private static readonly string root = Application.persistentDataPath + "/SparseSpatialMap";

        public static List<MapMeta> LoadAll()
        {
            //Debug.Log(root);
            var metas = new List<MapMeta>();
            var dirRoot = GetRootPath();
            try
            {
                foreach (var path in Directory.GetFiles(dirRoot, "*.meta"))
                {
                    try
                    {
                        metas.Add(JsonUtility.FromJson<MapMeta>(File.ReadAllText(path)));
                    }
                    catch (System.Exception e)
                    {
                        Debug.LogError(e.Message);
                    }
                }
            }
            catch (System.Exception e)
            {
                Debug.LogError(e.Message);
            }
            return metas;
        }


        public static bool Save(MapMeta meta, FileNameType fileNameType = FileNameType.ID)
        {
            try
            {
                switch (fileNameType)
                {
                    case FileNameType.ID:
                        File.WriteAllText(GetPath(meta.Map.ID), JsonUtility.ToJson(meta, true));
                        break;
                    case FileNameType.Name:
                        File.WriteAllText(GetPath(meta.Map.Name), JsonUtility.ToJson(meta, true));
                        break;
                }
            }
            catch (System.Exception e)
            {
                Debug.LogError(e.Message);
                return false;
            }
            return true;
        }



        public static bool Save(PointData data)
        {
            try
            {
                File.WriteAllText(GetPathTxt(data.mapName + "_PointCloud"), JsonConvert.SerializeObject(data));
            }
            catch (System.Exception e)
            {
                Debug.LogError(e.Message);
                return false;
            }
            return true;
        }

        public static List<PointData> Load_PointCloud<PointData>()
        {
            var dirRoot = GetRootPath();
            var datas = new List<PointData>();
            try
            {
                foreach (var path in Directory.GetFiles(dirRoot, "*.txt"))
                {
                    try
                    {
                        //Debug.Log(path); 
                        datas.Add(JsonConvert.DeserializeObject<PointData>(File.ReadAllText(path)));
                    }
                    catch (System.Exception e)
                    {
                        Debug.LogError(e.Message);
                    }
                }
            }
            catch (System.Exception e)
            {
                Debug.LogError(e.Message);
            }
            return datas;
        }




        public static bool Delete(MapMeta meta)
        {
            if (!File.Exists(GetPath(meta.Map.ID)))
            {
                return false;
            }
            try
            {
                File.Delete(GetPath(meta.Map.ID));
            }
            catch (System.Exception e)
            {
                Debug.LogError(e.Message);
                return false;
            }
            return true;
        }

        private static string GetRootPath()
        {
            var path = root;
            if (!File.Exists(path))
            {
                Directory.CreateDirectory(path);
            }
            return path;
        }

        private static string GetPath(string id)
        {
            return GetRootPath() + "/" + id + ".meta";
        }

        private static string GetPathTxt(string id)
        {
            return GetRootPath() + "/" + id + ".txt";
        }

    }
}

 

评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ThursdayGame

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值