【unity】 Loom实现多线程
通常情况下,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);
}