本文主要是介绍Unity - coroutines 协程揭秘,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
孙广东 2015.6.16
理解UNity协同系统将帮助您更有效地使用它。
UNity协同程序可能似乎有点奇怪,很少有人知道他们是如何工作的。
了解IEnumerator
你能猜到,协同工作与 IEnumerator接口和 CLR 枚举实现有关,所以我们首先应该明白这些东西是如何工作。最初, IEnumerator和IEnumerable应该处理的集合的Item,如lists or arrays。想象一下,一个字符串,也可以是一个字符数组。循环访问集合的常用方法是使用foreach语句:
foreach(char c in "coroutine")Debug.Log(c);
这整洁的声明和IEnumerable
接口,只知道如何提供IEnumerator
一起工作。enumerator 告诉小 “story” 的如何遍历当前集合 (它可以提供当前元素,并知道如何移动到下一个)。总体来看,它看上去有点像这样:
class Enumerable // Typically implements IEnumerable or IEnumerable<T>
{public Enumerator GetEnumerator() {...}
}class Enumerator // Typically implements IEnumerator or IEnumerator<T>
{public IteratorVariableType Current { get {...} }public bool MoveNext() {...}
}
这么说,这是foreach
语句, 它在编译时:
using (var enumerator = "coroutine".GetEnumerator())while (enumerator.MoveNext()){var element = enumerator.Current;Console.WriteLine (element);}
IEnumerator
也实现IDisposable
,所以它是完全合法using
语句内使用它。
迭代器 Iterators
这是关键的话题。我们实际上几乎就是协同程序本身。在我们的示例中,foreach 语句是枚举的enumeration的一个消费者,而迭代器是enumeration的生产者。看看代码:
...
void Start()
{foreach(int fibNum in Fibs(6))Debug.Log(fibNum);
}IEnumerable<int> Fibs(int fibCount)
{for(int i = 0, prevFib = 1, curFib = 1; i < fibCount; i++){yield return prevFib;int newFib = prevFib + curFib;prevFib = curFib;curFib = newFib;}
}
看看线 # 12。yield
语句, IEnumerable
返回类型,以及是什么使得它都如此特别。return
语句给你一个 该方法返回yield return 集合
的下一个元素,你问此方法以yield 从enumerator 的值。每次遇到了yield ,控制返回到调用方,和它返回到 calee,在下一次它捡起并继续。编译器实际上将这种方法 (Fibs(...)
) 转换成拼接这个yield return
的逻辑, 纳入MoveNext
方法和Current
属性的私有类。当调用此方法时,你只是 实例化编译器编写的类 和无代码实际运行! 只有当你开始枚举此“on-the-fly”集合时,将运行您的代码!
迭代器可以有一个或多个yield
语句并且应该返回以下的接口来绕过编译器错误:
System.Collections.IEnumerable
System.Collections.Generic.IEnumerable<T>
System.Collections.IEnumerator
System.Collections.Generic.IEnumerator<T>
也是yield break
的语句,它允许终止迭代,所以没有更多的元素从enumerator返回。
回到协同程序
让我们看看此enumerator 逻辑如何转化为Unity协同程序。你可能已经注意到,协同程序通常yield return
不同时间跨度或null,
如果代码需要继续在下一帧。这是因为Unity需要这些返回的值,以知道何时调用MoveNext()
方法! 当你写你协同程序。例如以这段代码:
void Start()
{WaitForSeconds delay = new WaitForSeconds(1f);StartCoroutine(TestCoroutine(delay));
}IEnumerator TestCoroutine(WaitForSeconds delay)
{while (true){Debug.Log("tick");yield return delay;}
}
正如你所看到的还有一个yield return
语句返回传递的 WaitForSeconds
对象,用于延迟。“tick” 消息将立即写入控制台,和在那之后的每一秒。尝试移动Debug.Log(..)
调用后yield return
语句,看看会发生什么。
让我们看看在调用TestCoroutine
方法时,会发生什么。
void Start()
{WaitForSeconds delay = new WaitForSeconds(1f);TestCoroutine(delay);
}
似乎没有什么发生,但我们实际上在堆中创建enumerator 对象。你也可以做出这样的事情:
void Start()
{IEnumerator rator = TestCoroutine(new WaitForSeconds(1f));for (int i = 0; i < 5; i++){rator.MoveNext();}
}
但无论如何它们确实很酷。
时刻牢记
- 创建协同是创建一个迭代器,通过虚构的collection of items 的过程。
- 迭代器返回值用于确定
MoveNext
方法到下一次调用的时间。 - 每次
StartCoroutine被
调用, 协同程序将分配在堆中分配一个 enumerator 对象。
希望这将帮助你更有效。你也可以打动你的同事与自定义协同执行!
最后,贴上一个自定义实现的协程程序:http://wiki.unity3d.com/index.php?title=CoroutineScheduler
这篇关于Unity - coroutines 协程揭秘的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!