好了,最近修改了下項目,總結下其中的坑,不過還是有點小瑕疵,,有時候會有一圈的點,不知道什麼原因,數據截取不到。暫停之後數據還在一直打印。有明白的交流下。
先看下效果圖:
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 的循環執行時間變短。
-
我剛開始使用了先實例化所有物體,進行隱藏,等到 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(() =>
// {
//把你需要執行的函數寫到這裏
// });
//});
}
}