好了,最近修改了下项目,总结下其中的坑,不过还是有点小瑕疵,,有时候会有一圈的点,不知道什么原因,数据截取不到。暂停之后数据还在一直打印。有明白的交流下。
先看下效果图:
Unity 效果图:
PointCloudViewer 效果图:(官方给的测试工具)
-
Unity 中的脚本是继承自 MonoBehaviour,其中内在的一些函数不能用在分线程中。由于接收串口传过来的数据太多,所以主线程只接收数据,接受完后将数据传递给分线程进行处理。Unity 中的 Debug.Log ,GetCompenent,SendMessage 等等不能写在分线程中,即使有的能写类似 Debug.Log 用于辅助查看信息是否出错,在后续运行的过程中也要去掉,否则会造成数据错误。
-
Mathf.Sin() Mathf.Cos() 参数都是弧度。重点强调,处理数据的时候自己想到了Mathf.Atan() 参数是弧度,实际应用计算位置坐标的时候又忘记了,导致效果一直不对。解析出来的是角度,通过距离和角度进行计算坐标时,使用时先将角度转为弧度再进行三角函数计算。
-
由于项目需求是接受完一条完整数据再进行实例化显示,而接收处理数据都在分线程中进行,接收完数据避免不了使用 Unity 内部封装的函数进行 UI 实例化,如果使用官方给的解决方案,脚本加到项目中,同时根据代码最后说明的将注释的那一段放到需要的脚本中即可使用。就会造成数据没有接收处理完就进行实例化,造成数据混乱。解决办法:不使用官方给的案例,依旧等到一条数据处理完然后将角度距离存储到对应的 List 中,在继承 MonoBehaviour 的执行脚本中的 Start 函数中通过 InvokeRepeating 函数中固定时间重复读取存储数据的 List 对物体位置进行设置。此外,注意,可能数据较多,需要使用对象池,否则 Unity 崩掉,我采用的是 Awake 中事先实例化固定的物体,以固定长度不断从 List 中进行读取位置信息赋值给实例化好的物体,然后从 List 中将已经使用的位置信息移除掉,后面新的位置继续重新赋值给实例化好的物体,这样就不用重复实例化和销毁,不用重复操作内存。
-
注意多观察雷达数据,我一直以为输出的角度只可能有 0 - 360 度,后面发现还有零度,所以处理角度的时候要特别注意。
-
鉴于第三点钟我使用的减少操作内存的方法,存在一些问题,例如,读取存入 List 中的数据比较多,而进行一处的速度较慢,我才用了 InvokeRepeating 的循环执行时间变短。
扫描二维码关注公众号,回复: 5535718 查看本文章 -
我刚开始使用了先实例化所有物体,进行隐藏,等到 List 中有数据信息再把物体显示出来,后来出现一闪一闪的情况。所以就不进行隐藏和显示,只是不断更新位置信息即可。
-
观察数据中出现其他情况。起始角和结束角一样的时候,以前的程序只存了一个距离,所以造成后面赋值的时候,角度数量和距离数量对不上,所以距离存储多少个要根据返回出的数据长度进行添加。
-
同时注意 List 的定期清理,否则信息越来越多,容易出错。‘
-
你想要看你的位置显示信息对不对,可以在差不多相同的环境中,分别运行 Unity 项目和 PointCloudViewer 测试工具,同时我还发现 PointCloudViewer 的界面可以通过滑动鼠标滚轮进行拉近和拉远,左上角的数据保存可以在运行的时候导出,数据是 0 - 360 度对应的距离,不会重复输出 0 - 360度,即只有一个循环。你可以导出来和你自己打印出来的数据进行大致对比。
-
项目更新了,可以运行 Scene1 查看做出的雷达显示效果图,类似 PointCloudViewer 的效果。项目链接
官方给出的解决分线程中无法使用 Mono 内部函数的代码:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using System.Threading;
using System.Linq;
/// <summary>
/// 解决开启的线程不能执行其他程序的脚本
/// </summary>
public class Loom : MonoBehaviour
{
public static int maxThreads = 8;
static int numThreads;
private static Loom _current;
private int _count;
public static Loom Current
{
get
{
Initialize();
return _current;
}
}
void Awake()
{
_current = this;
initialized = true;
}
static bool initialized;
static void Initialize()
{
if (!initialized)
{
if (!Application.isPlaying)
return;
initialized = true;
var g = new GameObject("Loom");
_current = g.AddComponent<Loom>();
}
}
private List<Action> _actions = new List<Action>();
public struct DelayedQueueItem
{
public float time;
public Action action;
}
private List<DelayedQueueItem> _delayed = new List<DelayedQueueItem>();
List<DelayedQueueItem> _currentDelayed = new List<DelayedQueueItem>();
public static void QueueOnMainThread(Action action)
{
QueueOnMainThread(action, 0f);
}
public static void QueueOnMainThread(Action action, float time)
{
if (time != 0)
{
lock (Current._delayed)
{
Current._delayed.Add(new DelayedQueueItem { time = Time.time + time, action = action });
}
}
else
{
lock (Current._actions)
{
Current._actions.Add(action);
}
}
}
public static Thread RunAsync(Action a)
{
Initialize();
while (numThreads >= maxThreads)
{
Thread.Sleep(1);
}
Interlocked.Increment(ref numThreads);
ThreadPool.QueueUserWorkItem(RunAction, a);
return null;
}
private static void RunAction(object action)
{
try
{
((Action)action)();
}
catch
{
}
finally
{
Interlocked.Decrement(ref numThreads);
}
}
void OnDisable()
{
if (_current == this)
{
_current = null;
}
}
// Use this for initialization
void Start()
{
}
List<Action> _currentActions = new List<Action>();
// Update is called once per frame
void Update()
{
lock (_actions)
{
_currentActions.Clear();
_currentActions.AddRange(_actions);
_actions.Clear();
}
foreach (var a in _currentActions)
{
a();
}
lock (_delayed)
{
_currentDelayed.Clear();
_currentDelayed.AddRange(_delayed.Where(d => d.time <= Time.time));
foreach (var item in _currentDelayed)
_delayed.Remove(item);
}
foreach (var delayed in _currentDelayed)
{
delayed.action();
}
// 在另外一个出错脚本中把下面代码复制过去
//Loom.RunAsync(() =>
//{
// Loom.QueueOnMainThread(() =>
// {
//把你需要执行的函数写到这里
// });
//});
}
}