Unity Loom 插件使用
最近在做资源更新时,需要显示现在的进度调。我在Unity中开启了一个线程来做下载任务,然后实时刷新下载进度。然后Unity报了一个错误。get_isActiveAndEnabled can only be called from the main thread.大体意思是Unity中的组件只能运行在Unity的主线程中,无法在我新开的线程中调用Unity的组件。百度了一下,找到了Loom这个插件。感觉真的很好用,错误不见了。
这里做下记录。
Loom插件就一个脚本导入到Unity中就行了。具体脚本内容如下
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using System.Threading;
using System.Linq;
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;
public static void Initialize()
{
if (!initialized)
{
if (!Application.isPlaying)
return;
initialized = true;
var g = new GameObject("Loom");
_current = g.AddComponent<Loom>();
#if !ARTIST_BUILD
UnityEngine.Object.DontDestroyOnLoad(g);
#endif
}
}
public struct NoDelayedQueueItem
{
public Action<object> action;
public object param;
}
private List<NoDelayedQueueItem> _actions = new List<NoDelayedQueueItem>();
public struct DelayedQueueItem
{
public float time;
public Action<object> action;
public object param;
}
private List<DelayedQueueItem> _delayed = new List<DelayedQueueItem>();
List<DelayedQueueItem> _currentDelayed = new List<DelayedQueueItem>();
public static void QueueOnMainThread(Action<object> taction, object tparam)
{
QueueOnMainThread(taction, tparam, 0f);
}
public static void QueueOnMainThread(Action<object> taction, object tparam, float time)
{
if (time != 0)
{
lock (Current._delayed)
{
Current._delayed.Add(new DelayedQueueItem { time = Time.time + time, action = taction, param = tparam });
}
}
else
{
lock (Current._actions)
{
Current._actions.Add(new NoDelayedQueueItem { action = taction, param = tparam });
}
}
}
public static Thread RunAsync(Action a)
{
Initialize();
while (numThreads >= maxThreads)
{
Thread.Sleep(100);
}
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<NoDelayedQueueItem> _currentActions = new List<NoDelayedQueueItem>();
// Update is called once per frame
void Update()
{
if (_actions.Count > 0)
{
lock (_actions)
{
_currentActions.Clear();
_currentActions.AddRange(_actions);
_actions.Clear();
}
for (int i = 0; i < _currentActions.Count; i++)
{
_currentActions.action(_currentActions.param);
}
}
if (_delayed.Count > 0)
{
lock (_delayed)
{
_currentDelayed.Clear();
_currentDelayed.AddRange(_delayed.Where(d => d.time <= Time.time));
for (int i = 0; i < _currentDelayed.Count; i++)
{
_delayed.Remove(_currentDelayed);
}
}
for (int i = 0; i < _currentDelayed.Count; i++)
{
_currentDelayed.action(_currentDelayed.param);
}
}
}
}
代码也就100多行,主要是两个比较主要的方法
RunAsync(Action a)和QueueOnMainThread(Action<object> taction, object tparam)
开启一个线程然后在Loom.RunAsyn()中调用需要回到Unity主线程更新界面时调用QueueOnMainThread()即可。简单好用。
下面贴一下 不用Loom报错的代码和用Loom后的代码
public Text mText;
void Start ()
{
Thread thread = new Thread(RefreshText);
thread.Start();
}
void Update ()
{
}
private void RefreshText()
{
mText.text = &#34;Hello Loom!&#34;;
}运行这段代码Unity会报上面的错误,意思是我在我自己开的线程中调用了Unity组件。
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using System.Threading;
public class testLoom : MonoBehaviour
{
public Text mText;
void Start ()
{
// 用Loom的方法调用一个线程
Loom.RunAsync(
() =>
{
Thread thread = new Thread(RefreshText);
thread.Start();
}
);
}
void Update ()
{
}
private void RefreshText()
{
// 用Loom的方法在Unity主线程中调用Text组件
Loom.QueueOnMainThread((param) =>
{
mText.text = &#34;Hello Loom!&#34;;
},null);
}
}
这样就是不会报错。
短短的100多行代码就搞定了这个问题,不得不佩服大神们的思路,有时间可以好好研究下Loom的实现的方法。
页:
[1]