赞
踩
介绍一个插件Loom可以让你在Unity使用多线程处理UnityEngine的逻辑(包含Unity的API)。
Unity中的线程不能使用Unity的对象,但可以使用Unity的值类型变量,如Vector3等。这样就使得线程在Unity中显的很鸡肋和蹩脚,因为很多函数很都是UnityEngine类或函数的调用的,对于哪些是可以在多线程使用,如下总结:
1. 变量(都能指向相同的内存地址)都是共享的
2. 不是UnityEngine的API能在分线程运行
3. UnityEngine定义的基本结构(int,float,Struct定义的数据类型)可以在分线程计算,如 Vector3(Struct)可以 , 但Texture2d(class,根父类为Object)不可以。
4. UnityEngine定义的基本类型的函数可以在分线程运行,如
int i = 99;
print (i.ToString());
Vector3 x = new Vector3(0,0,9);
x.Normalize();
类的函数不能在分线程运行
obj.name
实际是get_name函数,分线程报错误:get_name can only be called from the main thread.
Texture2D tt = new Texture2D(10,10);
实际会调用UnityEngine里的Internal_Create,分线程报错误:Internal_Create can only be called from the main thread.
其他transform.position,Texture.Apply()等等都不能在分线程里运行。
结论: 分线程可以做 基本类型的计算, 以及非Unity(包括.Net及SDK)的API。
我觉得Unity做了这个限制,主要是Unity的函数执行机制是帧序列调用,甚至连Unity的协程Coroutine的执行机制都是确定的,如果可以使用多线程访问UnityEngine的对象和api就得考虑同步问题了,也就是说Unity其实根本没有多线程的机制,协程只是达到一个延时或者是当指定条件满足是才继续执行的机制。
以下是插件的介绍:
Our class is called Loom. Loom lets you easily run code on another thread and have that other thread run code on the main game thread when it needs to.
There are only two functions to worry about:
You access Loom using Loom.Current - it deals with creating an invisible game object to interact with the games main thread.
- 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;
-
- 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();
- }
-
-
-
- }
- }
RunAsync(Action)和QueueOnMainThread(Action, [optional] float time) 就可以轻松实现一个函数的两段代码在C#线程和Unity的主线程中交叉运行。原理也很简单:用线程池去运行RunAsync(Action)的函数,在Update中运行QueueOnMainThread(Acition, [optional] float time)传入的函数。
- //Scale a mesh on a second thread
- void ScaleMesh(Mesh mesh, float scale)
- {
- //Get the vertices of a mesh
- var vertices = mesh.vertices;
- //Run the action on a new thread
- Loom.RunAsync(()=>{
- //Loop through the vertices
- for(var i = 0; i < vertices.Length; i++)
- {
- //Scale the vertex
- vertices[i] = vertices[i] * scale;
- }
- //Run some code on the main thread
- //to update the mesh
- Loom.QueueOnMainThread(()=>{
- //Set the vertices
- mesh.vertices = vertices;
- //Recalculate the bounds
- mesh.RecalculateBounds();
- });
-
- });
- }
有了Loom这个工具类,在很多涉及UnityEngine对象的耗时计算还是可以得到一个解决方法的:
如在场景中用A*算法进行大量的数据计算
变形网格中操作大量的顶点
持续的要运行上传数据到服务器
二维码识别等图像处理
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。