通常情況下,unity中在子執行緒中改變變數的值,但是子執行緒尚未結束時,主執行緒無法使用該變數。
因此使用Loom作為中介,子執行緒呼叫並傳值給Loom,Loom呼叫主執行緒的API。
實現步驟
建立Loom空物體,並掛載Loom指令碼
//Loom.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class Loom : MonoBehaviour
{
static bool isInitialized;
private static Loom instance;
public static Loom Instance
{
get
{
Initialize();
return instance;
}
}
public static void Initialize()
{
if (!isInitialized)
{
if (!Application.isPlaying)
return;
isInitialized = true;
var obj = new GameObject("Loom");
instance = obj.AddComponent<Loom>();
DontDestroyOnLoad(obj);
}
}
private void Awake()
{
instance = this;
isInitialized = true;
}
struct NoDelayQueueItem
{
public Action<int> action;
public int param;
}
List<NoDelayQueueItem> listNoDelayActions = new List<NoDelayQueueItem>();
struct DelayQueueItem
{
public Action<int> action;
public int param;
public float time;
}
List<DelayQueueItem> listDelayedActions = new List<DelayQueueItem>();
public static void QueueOnMainThread(Action<int> taction, int param)
{
QueueOnMainThread(taction, param, 0f);
}
public static void QueueOnMainThread(Action<int> action, int param, float time)
{
if (time != 0)
{
lock (Instance.listDelayedActions)
{
Instance.listDelayedActions.Add(new DelayQueueItem { time = Time.time + time, action = action, param = param });
}
}
else
{
lock (Instance.listNoDelayActions)
{
Instance.listNoDelayActions.Add(new NoDelayQueueItem { action = action, param = param });
}
}
}
List<NoDelayQueueItem> currentActions = new List<NoDelayQueueItem>();
List<DelayQueueItem> currentDelayed = new List<DelayQueueItem>();
private void Update()
{
//無延遲的執行佇列中存在任務
if (listNoDelayActions.Count > 0)
{
lock (listNoDelayActions)
{
//把所有任務放入當前執行佇列
currentActions.Clear();
currentActions.AddRange(listNoDelayActions);
listNoDelayActions.Clear();
}
//挨個執行完任務
for (int i = 0; i < currentActions.Count; i++)
{
currentActions[i].action(currentActions[i].param);
}
}
//有延遲的執行佇列中存在任務
if (listDelayedActions.Count > 0)
{
lock (listDelayedActions)
{
//將此刻之前的所有未完成的任務放入當前執行佇列
currentDelayed.Clear();
currentDelayed.AddRange(listDelayedActions.Where(d => Time.time >= d.time));
for (int i = 0; i < currentDelayed.Count; i++)
{
listDelayedActions.Remove(currentDelayed[i]);
}
}
for (int i = 0; i < currentDelayed.Count; i++)
{
currentDelayed[i].action(currentDelayed[i].param);
}
}
}
private void OnDisable()
{
if (instance == this)
{
instance = null;
}
}
}
子執行緒中呼叫Loom類,Loom中執行主執行緒API
private void Awake()
{
button.GetComponent<UnityEngine.UI.Button>().onClick.AddListener(OnPointClick);
onPositionChangedEvent += onPositionChanged;
onPositionChanged();
}
public UnityEngine.UI.Button button;
public Action onPositionChangedEvent;
//子執行緒會改變此變數,此變數發生變化時,會執行委託(主執行緒API)
private int _position = 10;
public int Position
{
get { return _position; }
set
{
_position = value;
if (onPositionChangedEvent != null)
{
onPositionChangedEvent();
}
}
}
Thread thread = null;
int direct = 1;
// button開啟子執行緒,此執行緒一直開啟
public void OnPointClick()
{
if (thread == null)
{
thread = new Thread(() =>
{
while (true)
{
if (Position < -100)
{
direct = 1;
}
else if (Position > 100)
{
direct = -1;
}
//子執行緒更改的變數,此變數每次發生改變時需要在主執行緒中使用
Position += 1 * direct;
Thread.Sleep(100);
}
});
thread.IsBackground = true;
thread.Start();
}
}
//Position發生變化時,就會呼叫該方法
private void onPositionChanged()
{
//Loom會在子執行緒執行,但是可以呼叫主執行緒API
Loom.QueueOnMainThread((Position) =>
{
transform.position = new Vector3(Position, 0, 0);
}, Position);
}