尊重原创,转载请在文首注明出处:http://blog.youkuaiyun.com/cai612781/article/details/78992805
一,定义
Unity协程(Coroutine),不是卖机票的携程,是一种类似子线程的机制,可以用来实现一些延时处理的需求,c#中通过yield return语句配合可以中断执行,延时一定时间后从中断处继续执行。
二,注意
协程不是线程,协程不是线程,协程不是线程,协程还是在主线程里!!!
三,语法
脚本需要继承MonoBehaviour,提供了两个方法StartCoroutine(IEnumerator routine)或者StartCoroutine(string methodName)开启一个协程,以及对应的StopCoroutine(IEnumerator routine)或StopCoroutine(string methodName)关闭协程。推荐使用前者,因为传递方法名需要用到反射机制,影响性能。
四,常见应用
1)延时(别想歪)
- using UnityEngine;
- using System.Collections;
- public class ExampleClass : MonoBehaviour
- {
- IEnumerator Start()
- {
- Debug.Log("Start--" + Time.time);
- yield return StartCoroutine(WaitAndLog);
- Debug.Log("End--" + Time.time);
- }
- IEnumerator WaitAndLog()
- {
- yield return new WaitForSeconds(3);
- Debug.Log("Wait--" + Time.time);
- }
- }
- using UnityEngine;
- using System.Collections;
- public class ExampleClass : MonoBehaviour
- {
- private int _num,_total;
- Start()
- {
- StartCoroutine(LoadAssets);
- }
- IEnumerator LoadAssets()
- {
- while(_num < _total)
- {
- _num++;
- Debug.Log("Load an Asset");
- yield return null;
- }
- }
- }
五,yield
yield语句用于暂停协程的执行,yield return 的值决定什么时候恢复协程的执行。yield return 返回一个IEnumerator对象,当该对象的MoveNext()返回false,即该对象的Current已迭代到最后一个元素时,才会执行yield return后的代码。
yield语句的返回有如下类型:
yield return null;//下一帧再继续往下执行
yield return new WaitForFixedUpdate();//等到下一次调用FixedUpdate再往下执行
yield return new WaitForSceonds(n);//等待n秒再往下执行
yield return StartCoroutine(Method);//开启另一个协程,直到Method执行完毕再往下执行
yield return new WaitForEndOfFrame();//等到该帧结束再往下执行
yield return WWW;//等待资源加载完成再往下执行
yield break;//结束协程
六,执行顺序
协程是每帧lateUpdate前执行yield return之前的代码,lateUpdate后执行yield return之后的代码。
不同yield return返回值的执行顺序,通过官方文档的一张流程图可以清晰地了解:
七,原理
Unity的协程应该是一个扩展成支持嵌套的迭代器(IEnumator)。
它将c# IEnumerator封装为Coroutine对象,它记录了该协程的上一级协程是谁,当这个协程执行完成,会继续执行上一级协程。
当Unity Coroutine调用IEnumerator的MoveNext返回一个新Coroutine时,Unity设置新对象的上级Coroutine为当前Coroutine,同时将新Coroutine压入队列,通过此实现嵌套。
然后Unity在主线程中定时检测当前每个等待的Coroutine,满足条件则执行。
八,在Editor下使用Coroutine
编辑器下因为没有运行Unity,也没有创建GameObject,因此也无法通过Monobehaviour的StartCoroutine创建协程。
以下代码来自网上不知哪位大牛,根据Unity协程原理另外实现了个协程:
1,EditorCoroutine协程类实现了IEnumerator接口,在MoveNext函数中实现了嵌套调用协程
- public class EditorCoroutine : IEnumerator
- {
- private Stack<IEnumerator> executionStack;
- public EditorCoroutine(IEnumerator iterator)
- {
- this.executionStack = new Stack<IEnumerator>();
- this.executionStack.Push(iterator);
- }
- public bool MoveNext()
- {
- IEnumerator i = this.executionStack.Peek();
- if (i.MoveNext())
- {
- object result = i.Current;
- if (result != null && result is IEnumerator)
- {
- this.executionStack.Push((IEnumerator)result);
- }
- return true;
- }
- else
- {
- if (this.executionStack.Count > 1)
- {
- this.executionStack.Pop();
- return true;
- }
- }
- return false;
- }
- public void Reset()
- {
- throw new System.NotSupportedException("This Operation Is Not Supported.");
- }
- public object Current
- {
- get { return this.executionStack.Peek().Current; }
- }
- public bool Find(IEnumerator iterator)
- {
- return this.executionStack.Contains(iterator);
- }
- }
- public static class EditorCoroutineRunner
- {
- private static List<EditorCoroutine> editorCoroutineList;
- private static List<IEnumerator> buffer;
- public static IEnumerator StartEditorCoroutine(IEnumerator iterator)
- {
- if (editorCoroutineList == null)
- {
- editorCoroutineList = new List<EditorCoroutine>();
- }
- if (buffer == null)
- {
- buffer = new List<IEnumerator>();
- }
- if (editorCoroutineList.Count == 0)
- {
- EditorApplication.update += Update;
- }
- buffer.Add(iterator);
- return iterator;
- }
- private static bool Find(IEnumerator iterator)
- {
- foreach (EditorCoroutine editorCoroutine in editorCoroutineList)
- {
- if (editorCoroutine.Find(iterator))
- {
- return true;
- }
- }
- return false;
- }
- private static void Update()
- {
- editorCoroutineList.RemoveAll
- (
- coroutine => { return coroutine.MoveNext() == false; }
- );
- if (buffer.Count > 0)
- {
- foreach (IEnumerator iterator in buffer)
- {
- if (!Find(iterator))
- {
- editorCoroutineList.Add(new EditorCoroutine(iterator));
- }
- }
- buffer.Clear();
- }
- if (editorCoroutineList.Count == 0)
- {
- EditorApplication.update -= Update;
- }
- }
- }
- using UnityEngine;
- using System.Collections;
- public class ExampleClass
- {
- Start()
- {
- EditorCoroutineRunner.StartEditorCoroutine(Routine);
- }
- IEnumerator Routine()
- {
- Debug.Log("Routine");
- yield return null;
- }
- }