找回密码
 立即注册
查看: 631|回复: 0

Unity Loom 插件使用

[复制链接]
发表于 2020-12-24 09:04 | 显示全部楼层 |阅读模式
最近在做资源更新时,需要显示现在的进度调。我在Unity中开启了一个线程来做下载任务,然后实时刷新下载进度。然后Unity报了一个错误。get_isActiveAndEnabled can only be called from the main thread.
大体意思是Unity中的组件只能运行在Unity的主线程中,无法在我新开的线程中调用Unity的组件。百度了一下,找到了Loom这个插件。感觉真的很好用,错误不见了。
这里做下记录。
Loom插件就一个脚本导入到Unity中就行了。具体脚本内容如下
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System;
  5. using System.Threading;
  6. using System.Linq;
  7.     public class Loom :MonoBehaviour
  8.     {
  9.         public static int maxThreads = 8;
  10.         static int numThreads;
  11.         private static Loom _current;
  12.         //private int _count;
  13.         public static Loom Current
  14.         {
  15.             get
  16.             {
  17.                 Initialize();
  18.                 return _current;
  19.             }
  20.         }
  21.         void Awake()
  22.         {
  23.             _current = this;
  24.             initialized = true;
  25.         }
  26.         static bool initialized;
  27.         public static void Initialize()
  28.         {
  29.             if (!initialized)
  30.             {
  31.                 if (!Application.isPlaying)
  32.                     return;
  33.                 initialized = true;
  34.                 var g = new GameObject("Loom");
  35.                 _current = g.AddComponent<Loom>();
  36. #if !ARTIST_BUILD
  37.                 UnityEngine.Object.DontDestroyOnLoad(g);
  38. #endif
  39.             }
  40.         }
  41.         public struct NoDelayedQueueItem
  42.         {
  43.             public Action<object> action;
  44.             public object param;
  45.         }
  46.         private List<NoDelayedQueueItem> _actions = new List<NoDelayedQueueItem>();
  47.         public struct DelayedQueueItem
  48.         {
  49.             public float time;
  50.             public Action<object> action;
  51.             public object param;
  52.         }
  53.         private List<DelayedQueueItem> _delayed = new List<DelayedQueueItem>();
  54.         List<DelayedQueueItem> _currentDelayed = new List<DelayedQueueItem>();
  55.         public static void QueueOnMainThread(Action<object> taction, object tparam)
  56.         {
  57.             QueueOnMainThread(taction, tparam, 0f);
  58.         }
  59.         public static void QueueOnMainThread(Action<object> taction, object tparam, float time)
  60.         {
  61.             if (time != 0)
  62.             {
  63.                 lock (Current._delayed)
  64.                 {
  65.                     Current._delayed.Add(new DelayedQueueItem { time = Time.time + time, action = taction, param = tparam });
  66.                 }
  67.             }
  68.             else
  69.             {
  70.                 lock (Current._actions)
  71.                 {
  72.                     Current._actions.Add(new NoDelayedQueueItem { action = taction, param = tparam });
  73.                 }
  74.             }
  75.         }
  76.         public static Thread RunAsync(Action a)
  77.         {
  78.             Initialize();
  79.             while (numThreads >= maxThreads)
  80.             {
  81.                 Thread.Sleep(100);
  82.             }
  83.             Interlocked.Increment(ref numThreads);
  84.             ThreadPool.QueueUserWorkItem(RunAction, a);
  85.             return null;
  86.         }
  87.         private static void RunAction(object action)
  88.         {
  89.             try
  90.             {
  91.                 ((Action)action)();
  92.             }
  93.             catch
  94.             {
  95.             }
  96.             finally
  97.             {
  98.                 Interlocked.Decrement(ref numThreads);
  99.             }
  100.         }
  101.         void OnDisable()
  102.         {
  103.             if (_current == this)
  104.             {
  105.                 _current = null;
  106.             }
  107.         }
  108.         // Use this for initialization
  109.         void Start()
  110.         {
  111.         }
  112.         List<NoDelayedQueueItem> _currentActions = new List<NoDelayedQueueItem>();
  113.         // Update is called once per frame
  114.         void Update()
  115.         {
  116.             if (_actions.Count > 0)
  117.             {
  118.                 lock (_actions)
  119.                 {
  120.                     _currentActions.Clear();
  121.                     _currentActions.AddRange(_actions);
  122.                     _actions.Clear();
  123.                 }
  124.                 for (int i = 0; i < _currentActions.Count; i++)
  125.                 {
  126.                     _currentActions[i].action(_currentActions[i].param);
  127.                 }
  128.             }
  129.             if (_delayed.Count > 0)
  130.             {
  131.                 lock (_delayed)
  132.                 {
  133.                     _currentDelayed.Clear();
  134.                     _currentDelayed.AddRange(_delayed.Where(d => d.time <= Time.time));
  135.                     for (int i = 0; i < _currentDelayed.Count; i++)
  136.                     {
  137.                         _delayed.Remove(_currentDelayed[i]);
  138.                     }
  139.                 }
  140.                 for (int i = 0; i < _currentDelayed.Count; i++)
  141.                 {
  142.                     _currentDelayed[i].action(_currentDelayed[i].param);
  143.                 }
  144.             }
  145.         }
  146.     }
复制代码
代码也就100多行,主要是两个比较主要的方法
RunAsync(Action a)和QueueOnMainThread(Action<object> taction, object tparam)

开启一个线程然后在Loom.RunAsyn()中调用需要回到Unity主线程更新界面时调用QueueOnMainThread()即可。简单好用。
下面贴一下 不用Loom报错的代码和用Loom后的代码
  1.     public Text mText;
  2.         void Start ()
  3.     {
  4.         Thread thread = new Thread(RefreshText);
  5.         thread.Start();
  6.         }
  7.        
  8.         void Update ()
  9.     {
  10.        
  11.         }
  12.     private void RefreshText()
  13.     {
  14.         mText.text = "Hello Loom!";
  15.     }
复制代码
运行这段代码Unity会报上面的错误,意思是我在我自己开的线程中调用了Unity组件。
  1. using UnityEngine;
  2. using System.Collections;
  3. using UnityEngine.UI;
  4. using System.Threading;
  5. public class testLoom : MonoBehaviour
  6. {
  7.     public Text mText;
  8.         void Start ()
  9.     {
  10.         
  11.         // 用Loom的方法调用一个线程
  12.         Loom.RunAsync(
  13.             () =>
  14.             {
  15.                Thread thread = new Thread(RefreshText);
  16.                 thread.Start();
  17.             }
  18.             );
  19.         }
  20.        
  21.         void Update ()
  22.     {
  23.        
  24.         }
  25.     private void RefreshText()
  26.     {  
  27.         // 用Loom的方法在Unity主线程中调用Text组件
  28.         Loom.QueueOnMainThread((param) =>
  29.             {
  30.                 mText.text = "Hello Loom!";
  31.             },null);
  32.     }
  33. }
复制代码
这样就是不会报错。
短短的100多行代码就搞定了这个问题,不得不佩服大神们的思路,有时间可以好好研究下Loom的实现的方法。
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-22 01:13 , Processed in 0.063869 second(s), 22 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表