unity3d 协程中触发检测能写成协程吗

Unity3D协程介绍 以及使用(2)
我的图书馆
Unity3D协程介绍 以及使用(2)
作者ChevyRay&,日,snaker7译 原文地址:
请注意:这个关于协程的教程共有两部分,这是第二部分,如果您未曾看过第一部分——,那么在阅读这部分内容之前建议您先了解一下。
计时器例子
在中,我们已经了解了协程如何让一个方法“暂停”下来,并且让它yield直到某些值到达我们给定的数值;并且利用它,我们还创建了一个很棒的计时器系统。协程一个很重要的内容是,它可以让普通的程序(比方说一个计时器)很容易地被抽象化并且被复用。
协程的参数
抽象化一个协程的第一个方法是给它传递参数,协程作为一个函数方法来说,它自然能够传递参数。这里有一个协程的例子,它在特定的地方输出了特定的信息。
嵌套的协程
在此之前,我们yield的时候总是用0(或者null),仅仅告诉程序在继续执行前等待下一帧。协程最强大的一个功能就是它们可以通过使用yield语句来相互嵌套。
眼见为实,我们先来创建一个简单的Wait()程序,不需要它做任何事,只需要在运行的时候等待一段时间就结束。
接下来我们要编写另一个协程,如下:
第二个方法用了yield,但它并没有用0或者null,而是用了Wait()来yield,这相当于是说,“不再继续执行本程序,直到Wait程序结束”。
现在,协程在程序设计方面的能力要开始展现了。
控制对象行为的例子
在最后一个例子中,我们就来看看协程如何像创建方便的计时器一样来控制对象行为。协程不仅仅可以使用可计数的时间来yield,它还能很巧妙地利用任何条件。将它与嵌套结合使用,你会得到控制游戏对象状态的最强大工具。
运动到某一位置
对于下面这个简单脚本组件,我们可以在Inspector面板中给targetPosition和moveSpeed变量赋值,程序运行的时候,该对象就会在协程的作用下,以我们给定的速度运动到给定的位置。
这样,这个程序并没有通过一个计时器或者无限循环,而是根据对象是否到达指定位置来yield。
按指定路径前进
我们可以让运动到某一位置的程序做更多,不仅仅是一个指定位置,我们还可以通过数组来给它赋值更多的位置,通过MoveToPosition() ,我们可以让它在这些点之间持续运动。
我还加了一个布尔变量,你可以控制在对象运动到最后一个点时是否要进行循环。
把Wait()程序加进来,这样就能让我们的对象在某个点就可以选择是否暂停下来,就像一个正在巡逻的AI守卫一样,这真是锦上添花啊!
如果你刚接触协程,我希望这两个教程能帮助你了解它们是如何工作的,以及如何来使用它们。以下是一些在使用协程时须谨记的其他注意事项:
l 在程序中调用StopCoroutine()方法只能终止以字符串形式启动(开始)的协程;l 多个协程可以同时运行,它们会根据各自的启动顺序来更新;l 协程可以嵌套任意多层(在这个例子中我们只嵌套了一层);l 如果你想让多个脚本访问一个协程,那么你可以定义静态的协程;l 协程不是多线程(尽管它们看上去是这样的),它们运行在同一线程中,跟普通的脚本一样;l 如果你的程序需要进行大量的计算,那么可以考虑在一个随时间进行的协程中处理它们;l IEnumerator类型的方法不能带ref或者out型的参数,但可以带被传递的引用;l 目前在Unity中没有简便的方法来检测作用于对象的协程数量以及具体是哪些协程作用在对象上。
如果您发现教程中存在问题和错误的信息,或者有任何建议又或者您想要在这里看到其他需要的教程,可以发或者在评论中留言。
TA的最新馆藏
喜欢该文的人也喜欢Unity协程无法同步返回的解决办法-GAD腾讯游戏开发者平台Unity中协程的主动调用 - CSDN博客
Unity中协程的主动调用
& &在项目中,有的时候需要手动控制协程的更新,也就是重写StartCoroutine方法。具体如下:
using System.C
using System.Collections.G
using UnityE
using System.R
public class CoroutineManager {
public Clock clock { }
private HashSet&CoroutineIter& cI
private List&CoroutineIter& itAddT
private List&CoroutineIter& itDelT
private CoroutineManager() {
cIters = new HashSet&CoroutineIter&();
itAddTemp = new List&CoroutineIter&();
itDelTemp = new List&CoroutineIter&();
clock = new Clock();
private static CoroutineManager instance =
public static CoroutineManager Instance {
if (instance == null) {
instance = new CoroutineManager();
public static float time {
return Instance.clock.
public CoroutineIter StartCoroutine(IEnumerator _it) {
var iter = new CoroutineIter(_it);
itAddTemp.Add(iter);
public void StopCoroutine(CoroutineIter cIter) {
if (cIter != null) {
itDelTemp.Add(cIter);
public bool doUpdate(float dt) {
if (itAddTemp.Count & 0) {
itAddTemp.ForEach(it =& { cIters.Add(it); });
itAddTemp.Clear();
if (clock.tick(dt) && cIters.Count & 0) {
foreach (var i in cIters) {
i.doUpdate(dt);
cIters.RemoveWhere(i =& i.isEnd);
if (itDelTemp.Count & 0) { // 删除
itDelTemp.ForEach(it =& { cIters.Remove(it); });
itDelTemp.Clear();
return cIters.Count & 0;
public class Clock {
public int frame { }
public float time { }
public float dt { }
public void reset() {
time = dt = 0;
public bool tick(float _dt) {
if (frame & Time.frameCount) {
frame = Time.frameC
public class CoroutineIter {
public bool isEnd { }
Stack&IEnumerator& stack = new Stack&IEnumerator&();
public CoroutineIter(IEnumerator _it) {
isEnd = it ==
public void doUpdate(float dt) {
if (!isEnd) {
if (it.MoveNext()) {
dealCurrent(it.Current);
it = stack.Count & 0 ? stack.Pop() :
isEnd = it ==
private void dealCurrent(object cur) {
if (it.Current is IEnumerator) {
stack.Push(it);
it = it.Current as IE
} else if (it.Current is WaitForSeconds) {
stack.Push(it);
it = new MyWaitForSecond(it.Current as WaitForSeconds);
class MyWaitForSecond : CustomYieldInstruction {
private float startT
CoroutineM
public MyWaitForSecond(WaitForSeconds wfs) {
duration = GetPrivateFieldValue&float&(wfs, &m_Seconds&);
cm = CoroutineManager.I
startTime = cm.clock.
public override bool keepWaiting {
return (cm.clock.time - startTime) &
private static T GetPrivateFieldValue&T&(object obj, string propName) {
if (obj == null)
throw new ArgumentNullException(&obj&);
Type t = obj.GetType();
FieldInfo fi =
while (fi == null && t != null) {
fi = t.GetField(propName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
t = t.BaseT
if (fi == null)
throw new ArgumentOutOfRangeException(&propName&,
string.Format(&Field {0} was not found in Type {1}&, propName,
obj.GetType().FullName));
return (T)fi.GetValue(obj);
调用方法:
using System.C
using UnityE
public class Testtest : MonoBehaviour {
CoroutineI
void Start() {
CoroutineManager.Instance.StartCoroutine(testx0());
//StartCoroutine(test0());
void Update() {
if (!CoroutineManager.Instance.doUpdate(Time.deltaTime)) {
Debug.LogError(&=======EEEEEEEE=======&);
private IEnumerator testx0() {
Debug.Log(&======={{{{ =======&);
var handler = CoroutineManager.Instance.StartCoroutine(testx1());
yield return new WaitForSeconds(3f);
CoroutineManager.Instance.StopCoroutine(handler);
Debug.Log(&======= }}}} =======&);
yield return new WaitForSeconds(1f);
Debug.Log(&======= End =======&);
private IEnumerator testx1() {
int i = 0;
while (true) {
yield return new WaitForSeconds(0.3f);
Debug.Log(&&&& & + (i++));
private IEnumerator test0() {
yield return test1();
Debug.Log(&&);
yield return test2();
Debug.Log(&&);
private IEnumerator test1() {
Debug.Log(&111111
: & + Time.time);
yield return new WaitForSeconds(1f);
Debug.Log(&111111
: & + Time.time);
private IEnumerator test2() {
Debug.Log(&222222
: & + Time.time);
yield return new WaitForSeconds(2f);
Debug.Log(&22222
: & + Time.time);
private IEnumerator testA() {
for (int i = 0; i & 3; i++) {
yield return new WaitForSeconds(i);
Debug.Log(&-1-
& + CoroutineManager.time);
Debug.Log(&-2-&);
Debug.Log(&-3-&);
yield return new CC();
Debug.Log(&-4-
& + CoroutineManager.time);
yield return new WaitUntil(delegate {
return CoroutineManager.time & 5;
Debug.Log(&-5-
&+ CoroutineManager.time);
yield return new WaitWhile(delegate {
return CoroutineManager.time & 8;
Debug.Log(&-6-
&+ CoroutineManager.time);
class CC : CustomYieldInstruction {
int i = 0;
public override bool keepWaiting {
Debug.Log(&CC & & + i);
return i++ & 10;
另外,协程遍历的方法,其实是一个树的深度遍历:
using UnityE
using UnityE
using NUnit.F
using System.C
using System.Collections.G
public class NewEditorTest {
public void EditorTest() {
//Iterate (test01 (0));
Iterate(testA(), obj=&{
if(obj is YieldInstruction){
Debug.LogFormat(&!!YI: & + obj.GetType().Name);
} else if(obj is CustomYieldInstruction) {
Debug.LogFormat(&!!CYI: {0}, isIt? {1} &, obj.GetType().Name, obj is IEnumerator);
private void Iterate(IEnumerator _it, System.Action&object& action = null) {
Stack&IEnumerator& stack = new Stack&IEnumerator& ();
var it = _
while (it != null) {
while (it.MoveNext ()) {
if (action != null)
action (it.Current);
if (it.Current is IEnumerator) {
stack.Push (it);
it = it.Current as IE
it = stack.Count & 0 ? stack.Pop() :
float time = 0;
private IEnumerator testA() {
for (int i = 0; i & 3; i++) {
Debug.Log (&-1-&);
yield return new WaitForSeconds (i);
Debug.Log (&-2-&);
Debug.Log (&-3-&);
yield return new CC ();
Debug.Log (&-4-&);
yield return new WaitUntil (delegate {
time += 0.3f;
Debug.Log(&&Time: & + time);
return time & 10;
Debug.Log (&-5-&);
yield return new WaitWhile (delegate {
time += 0.3f;
Debug.Log(&&Time: & + time);
return time & 15;
class CC : CustomYieldInstruction {
int i = 0;
public override bool keepWaiting {
Debug.Log (&CC & & + i);
return i++ & 10;
private IEnumerator test01(int l) {
Debug.Log (&----test01-{{{&);
print (1, l);
yield return test02 (l+1);
print (2, l);
print (3, l);
yield return test03(l+1);
print (4, l);
Debug.Log (&}}}test01-----&);
private IEnumerator test02(int l) {
Debug.Log (&----test02-{{{&);
print (1, l);
yield return test03 (l+1);
print (2, l);
print (3, l);
yield return test03(l+1);
print (4, l);
Debug.Log (&}}}test02-----&);
private IEnumerator test03(int l) {
Debug.Log (&----test03-{{{&);
print (1, l);
Debug.Log (&}}}test03-----&);
private void print(int num, int level) {
string rt = &&;
for (int i = 0; i & i++) {
Debug.LogFormat (&{0}{1}&, rt, num);
本文已收录于以下专栏:
相关文章推荐
百度百科中,定义协程为与子例程一样,协程(coroutine)也是一种程序组件。相对子例程而言,协程更为一般和灵活,但在实践中使用没有子例程那样广泛。协程源自 Simula 和 Modula-2 语言...
1. 首先说一下什么是协程
协同程序与线程差不多,也就是一条执行序列,拥有自己独立的栈,局部变量和指令指针,同时又与其它协同程序共享全局变量和其它大部分东西。线程与协同程...
qemu中Coroutine(协程)的实现与使用介绍
Num01–&协程的定义
协程,又称微线程,纤程。英文名Coroutine。
首先我们得知道协程是啥?协程其实可以认为是比线程更小的执行单元。 为啥说他是一个执行单元,因为他自带CPU上下文...
tp://www.chiark.greenend.org.uk/~sgtatham/coroutines.htmlby Simon Tatham 翻译:jimieweileIntroduction介绍...
(一)协程
开启方法:StartCoroutine(&函数名&);
结束方法StopCoroutine(&函数名&),StopAllCoroutines();
IEnumerator TestIEnu...
有些事情一帧之内只需要做一次,比如收发邮件时,一帧内数据层多次的变化,表现层只需要一次更新。
我采用协程做这个事情,为了避免发起多个协程,我记录了一个协程是否发起的变量。
但是u3d里的corou...
有些事情一帧之内只需要做一次,比如收发邮件时,一帧内数据层多次的变化,表现层只需要一次更新。
我采用协程做这个事情,为了避免发起多个协程,我记录了一个协程是否发起的变量。
但是u3d里的corou...
本文装载自:http.
        记得去年6月份刚开始实习的时候,当时要我写网络层的结构,用到了协程,当时有点懵,完全不知道Unity协程的执行机制是怎么...
生成器这东西在游戏中很常见,不如我们要随机产生敌人,就需要用到这东西。说白了,我就是需要一个东西在我的要求下,产生大量的对象。在Unity3D直接就提供接口能够轻松完成这一些,我开始还以为生成器是用U...
他的最新文章
讲师:宋宝华
讲师:何宇健
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)『 不要让任何事情成为你不去学习的理由!』
【转】【UNITY3D 游戏开发之六】Unity 协程Coroutine与Invoke
本站文章均为
原创,转载务必在明显处注明:
转载自【黑米GameDev街区】 原文链接:
这里Himi强调一点:Unity里面的协程并不是线程,协程是在unity主线程中运行的,每一帧中处理一次,而并不与主线程并行。这就意味着在协程之间并不存在着所谓线程间的同步和互斥问题,不会出现死锁。一般来说,访问同一个值也都是很安全的,用协程可以处理绝大多数的小问题,而且不用考虑复杂的线程间同步,还是很方便的。
要说协程的不足就是不能运用处理器的多核来提高处理性能,毕竟这个在运行时事实上是在一个线程中执行的。
【以下均为转载内容】
Unity3D –MonoBehaviour类Invoke,Coroutine :
Unity3D协程介绍 以及 使用 :
U3D 游戏开发中的 yield协程与消息传递 :
在Unity中,延时执行一段代码或者一个方法或者几个方法的情况非常普遍。
一般会用到Invoke和InvokeRepeating方法。顾名思义,第一个是执行一次,第二个是重复执行。
看下定义:
void Invoke(string methodName, float time);
第一个参数是方法名(注意是字符串形式),并不是更方便的委托。第二个是延时多少秒。只执行一次。
void InvokeRepeating(string methodName, float time, float repeatRate);
InvokeRepeating第二个参数是延时多少秒后开始,第三个参数是每次执行间隔的秒数。
你有没有发现这两个方法有个弊端,就是必须输入方法名!也就是我说,如果我想延时执行某段代码,必须把代码放在某个方法里,然后使用这Invoke或者InvokeRepeating方法来执行。
这样对于上下文变量、属性的引用就会尤为不便,而且不能传参数!!!尼玛,要他还有何用?
我猜你一定用过这样的方法。没错,“协同”,听起来还挺高大上的名字啊。
用StartCoroutine来执行一个以IEnumerator为返回值的方法,通常用于异步下载啊,等比较耗时又不能让游戏卡死的情况。
还有一个好的类WaitForSeconds,对,它就一个构造函数,用来延时的(延时………………比万艾可好用?比希爱力好用?)。
好了不废话了,以下是我自用的延时方法,放在一个类里以静态方法存在。可以在任何时候任何地方延时指定秒数的代码。
using UnityE
using System.C
public class DelayToInvoke : MonoBehaviour
public static IEnumerator DelayToInvokeDo(Action action, float delaySeconds)
yield return new WaitForSeconds(delaySeconds);
如何使用呢?
比如我点击NGUI的一个Button,则
void OnClick()
StartCoroutine(DelayToInvoke.DelayToInvokeDo(() =&
Application.LoadLevel(“Option”);
}, 0.1f));
C#中Invoke 和 BeginInvoke 的区别 :
Coroutine:
尊重他人的劳动,支持原创,转载请注明出处:http.
记得去年6月份刚开始实习的时候,当时要我写网络层的结构,用到了协程,当时有点懵,完全不知道Unity协程的执行机制是怎么样的,只是知道函数的返回值是IEnumerator类型,函数中使用yield return ,就可以通过StartCoroutine调用了。后来也是一直稀里糊涂地用,上网google些基本都是例子,很少能帮助深入理解Unity协程的原理的。
本文只是从Unity的角度去分析理解协程的内部运行原理,而不是从C#底层的语法实现来介绍(后续有需要再进行介绍),一共分为三部分:
线程(Thread)和协程(Coroutine)
Unity中协程的执行原理
IEnumerator & Coroutine
之前写过一篇《》主要是介绍TaskManager实现对协程的状态控制,没有Unity后台实现的协程的原理进行深究。虽然之前自己对协程还算有点了解了,但是对Unity如何执行协程的还是一片空白,在上看到两篇讲解Coroutine,如数家珍,当我看到Advanced Coroutine后面的Hijack类时,顿时觉得十分精巧,眼前一亮,遂动了写文分享之。
线程(Thread)和协程(Coroutine)
D.S.Qiu觉得使用协程的作用一共有两点:1)延时(等待)一段时间执行代码;2)等某个操作完成之后再执行后面的代码。总结起来就是一句话:控制代码在特定的时机执行。
很多初学者,都会下意识地觉得协程是异步执行的,都会觉得协程是C# 线程的替代品,是Unity不使用线程的解决方案。
所以首先,请你牢记:协程不是线程,也不是异步执行的。协程和 MonoBehaviour 的 Update函数一样也是在MainThread中执行的。使用协程你不用考虑同步和锁的问题。
Unity中协程的执行原理
<给出了协程的定义:
A coroutine is a function that is executed partially and, presuming suitable conditions are met, will be resumed at some point in the future until its work is done.
即协程是一个分部执行,遇到条件(yield return 语句)会挂起,直到条件满足才会被唤醒继续执行后面的代码。
Unity在每一帧(Frame)都会去处理对象上的协程。Unity主要是在Update后去处理协程(检查协程的条件是否满足),但也有写特例:
从上图的剖析就明白,协程跟Update()其实一样的,都是Unity每帧对会去处理的函数(如果有的话)。如果MonoBehaviour 是处于激活(active)状态的而且yield的条件满足,就会协程方法的后面代码。还可以发现:如果在一个对象的前期调用协程,协程会立即运行到第一个 yield return 语句处,如果是 yield return null ,就会在同一帧再次被唤醒。如果没有考虑这个细节就会出现一些奇怪的问题『1』。
『1』注 图和结论都是从 上得来的,经过下面的验证发现与实际不符,D.S.Qiu用的是Unity 4.3.4f1 进行测试的。经过测试验证,协程至少是每帧的LateUpdate()后去运行。
下面使用 yield return new WaitForSeconds(1f); 在Start,Update 和 LateUpdate 中分别进行测试:
using UnityE
using System.C
public class TestCoroutine : MonoBehaviour {
private bool isStartCall = false;
private bool isUpdateCall = false;
private bool isLateUpdateCall = false;
void Start () {
if (!isStartCall)
Debug.Log(&#8220;Start Call Begin&#8221;);
StartCoroutine(StartCoutine());
Debug.Log(&#8220;Start Call End&#8221;);
isStartCall = true;
IEnumerator StartCoutine()
Debug.Log(&#8220;This is Start Coroutine Call Before&#8221;);
yield return new WaitForSeconds(1f);
Debug.Log(&#8220;This is Start Coroutine Call After&#8221;);
void Update () {
if (!isUpdateCall)
Debug.Log(&#8220;Update Call Begin&#8221;);
StartCoroutine(UpdateCoutine());
Debug.Log(&#8220;Update Call End&#8221;);
isUpdateCall = true;
IEnumerator UpdateCoutine()
Debug.Log(&#8220;This is Update Coroutine Call Before&#8221;);
yield return new WaitForSeconds(1f);
Debug.Log(&#8220;This is Update Coroutine Call After&#8221;);
void LateUpdate()
if (!isLateUpdateCall)
Debug.Log(&#8220;LateUpdate Call Begin&#8221;);
StartCoroutine(LateCoutine());
Debug.Log(&#8220;LateUpdate Call End&#8221;);
isLateUpdateCall = true;
IEnumerator LateCoutine()
Debug.Log(&#8220;This is Late Coroutine Call Before&#8221;);
yield return new WaitForSeconds(1f);
Debug.Log(&#8220;This is Late Coroutine Call After&#8221;);
得到日志输入结果如下:
然后将yield return new WaitForSeconds(1f);改为 发现日志输入结果和上面是一样的,没有出现上面说的情况:
using UnityE
using System.C
public class TestCoroutine : MonoBehaviour {
private bool isStartCall = false;
private bool isUpdateCall = false;
private bool isLateUpdateCall = false;
void Start () {
if (!isStartCall)
Debug.Log(&#8220;Start Call Begin&#8221;);
StartCoroutine(StartCoutine());
Debug.Log(&#8220;Start Call End&#8221;);
isStartCall = true;
IEnumerator StartCoutine()
Debug.Log(&#8220;This is Start Coroutine Call Before&#8221;);
yield return null;
Debug.Log(&#8220;This is Start Coroutine Call After&#8221;);
void Update () {
if (!isUpdateCall)
Debug.Log(&#8220;Update Call Begin&#8221;);
StartCoroutine(UpdateCoutine());
Debug.Log(&#8220;Update Call End&#8221;);
isUpdateCall = true;
IEnumerator UpdateCoutine()
Debug.Log(&#8220;This is Update Coroutine Call Before&#8221;);
yield return null;
Debug.Log(&#8220;This is Update Coroutine Call After&#8221;);
void LateUpdate()
if (!isLateUpdateCall)
Debug.Log(&#8220;LateUpdate Call Begin&#8221;);
StartCoroutine(LateCoutine());
Debug.Log(&#8220;LateUpdate Call End&#8221;);
isLateUpdateCall = true;
IEnumerator LateCoutine()
Debug.Log(&#8220;This is Late Coroutine Call Before&#8221;);
yield return null;
Debug.Log(&#8220;This is Late Coroutine Call After&#8221;);
『今天意外发现Monobehaviour的函数执行顺序图,发现协程的运行确实是在LateUpdate之后,下面附上:』
增补于:03/12/
前面在介绍时,说到MonoBehaviour 没有针对特定的协程提供Stop方法,其实不然,可以通过MonoBehaviour enabled = false 或者 gameObject.active = false 就可以停止协程的执行『2』。
经过验证,『2』的结论也是错误的,正确的结论是,MonoBehaviour.enabled = false 协程会照常运行,但 gameObject.SetActive(false) 后协程却全部停止,即使在Inspector把
gameObject 激活还是没有继续执行:
using UnityE
using System.C
public class TestCoroutine : MonoBehaviour {
private bool isStartCall = false;
private bool isUpdateCall = false;
private bool isLateUpdateCall = false;
void Start () {
if (!isStartCall)
Debug.Log(&#8220;Start Call Begin&#8221;);
StartCoroutine(StartCoutine());
Debug.Log(&#8220;Start Call End&#8221;);
isStartCall = true;
IEnumerator StartCoutine()
Debug.Log(&#8220;This is Start Coroutine Call Before&#8221;);
yield return new WaitForSeconds(1f);
Debug.Log(&#8220;This is Start Coroutine Call After&#8221;);
void Update () {
if (!isUpdateCall)
Debug.Log(&#8220;Update Call Begin&#8221;);
StartCoroutine(UpdateCoutine());
Debug.Log(&#8220;Update Call End&#8221;);
isUpdateCall = true;
this.enabled = false;
IEnumerator UpdateCoutine()
Debug.Log(&#8220;This is Update Coroutine Call Before&#8221;);
yield return new WaitForSeconds(1f);
Debug.Log(&#8220;This is Update Coroutine Call After&#8221;);
yield return new WaitForSeconds(1f);
Debug.Log(&#8220;This is Update Coroutine Call Second&#8221;);
void LateUpdate()
if (!isLateUpdateCall)
Debug.Log(&#8220;LateUpdate Call Begin&#8221;);
StartCoroutine(LateCoutine());
Debug.Log(&#8220;LateUpdate Call End&#8221;);
isLateUpdateCall = true;
IEnumerator LateCoutine()
Debug.Log(&#8220;This is Late Coroutine Call Before&#8221;);
yield return null;
Debug.Log(&#8220;This is Late Coroutine Call After&#8221;);
先在Update中调用 this.enabled = 得到的结果:
然后把 this.enabled = 注释掉,换成 this.gameObject.SetActive(false); 得到的结果如下:
整理得到:通过设置MonoBehaviour脚本的enabled对协程是没有影响的,但如果 gameObject.SetActive(false) 则已经启动的协程则完全停止了,即使在Inspector把gameObject 激活还是没有继续执行。也就说协程虽然是在MonoBehvaviour启动的(StartCoroutine)但是协程函数的地位完全是跟MonoBehaviour是一个层次的,不受MonoBehaviour的状态影响,但跟MonoBehaviour脚本一样受gameObject 控制,也应该是和MonoBehaviour脚本一样每帧“轮询” yield 的条件是否满足。
yield 后面可以有的表达式:
a) null &#8211; the coroutine executes the next time that it is eligible
b) WaitForEndOfFrame &#8211; the coroutine executes on the frame, after all of the rendering and GUI is complete
c) WaitForFixedUpdate &#8211; causes this coroutine to execute at the next physics step, after all physics is calculated
d) WaitForSeconds &#8211; causes the coroutine not to execute for a given game time period
e) WWW &#8211; waits for a web request to complete (resumes as if WaitForSeconds or null)
f) Another coroutine &#8211; in which case the new coroutine will run to completion before the yielder is resumed
值得注意的是 WaitForSeconds()受Time.timeScale影响,当Time.timeScale = 0f 时,yield return new WaitForSecond(x) 将不会满足。
IEnumerator & Coroutine
协程其实就是一个IEnumerator(迭代器),IEnumerator 接口有两个方法 Current 和 MoveNext() ,前面介绍的
就是利用者两个方法对协程进行了管理,只有当MoveNext()返回 true时才可以访问 Current,否则会报错。迭代器方法运行到 yield return 语句时,会返回一个expression表达式并保留当前在代码中的位置。 当下次调用迭代器函数时执行从该位置重新启动。
Unity在每帧做的工作就是:调用 协程(迭代器)MoveNext() 方法,如果返回 true ,就从当前位置继续往下执行。
这里在介绍一个协程的交叉调用类 Hijack(参见附件):
using System.Collections.G
using System.L
using UnityE
using System.C
[RequireComponent(typeof(GUIText))]
public class Hijack : MonoBehaviour {
IEnumerator _countUp;
IEnumerator _countD
IEnumerator _
int value = 0;
void Start()
_countUp = CountUp();
_countDown = CountDown();
StartCoroutine(DoHijack());
void Update()
guiText.text = value.ToString();
void OnGUI()
if(GUILayout.Button(&#8220;Switch functions&#8221;))
if(_current == _countUp)
_current = _countD
_current = _countUp;
IEnumerator DoHijack()
while(true)
if(_current != null && _current.MoveNext())
yield return _current.C
yield return null;
IEnumerator CountUp()
float increment = 0;
while(true)
if(Input.GetKey(KeyCode.Q))
increment+=Time.deltaT
value += Mathf.RoundToInt(increment);
yield return null;
IEnumerator CountDown()
float increment = 0f;
while(true)
if(Input.GetKey(KeyCode.Q))
increment+=Time.deltaT
value -= Mathf.RoundToInt(increment);
yield return new WaitForSeconds(0.1f);
上面的代码实现是两个协程交替调用,对有这种需求来说实在太精妙了。
今天仔细看了下 有关Coroutine的两篇文章,虽然第一篇(参考①)现在验证的结果有很多错误,但对于理解协程还是不错的,尤其是当我发现Hijack这个脚本时,就迫不及待分享给大家。
本来没觉得会有上的文章会有错误的,无意测试了发现还是有很大的出入,当然这也不是说原来作者没有经过验证就妄加揣测,D.S.Qiu觉得很有可能是Unity内部的实现机制改变了,这种东西完全可以改动,Unity虽然开发了很多年了,但是其实在实际开发中还是有很多坑,越发觉得Unity的无力,虽说容易上手,但是填坑的功夫也是必不可少的。
看来很多结论还是要通过自己的验证才行,贸然复制粘贴很难出真知,切记!
? 【Unreal Engine 】 ? 【React Native】 ? 【Cocos Creator】? 【Cocos2dx】? 【C2dx-Lua】
Himi&#8217;s Book
小广告先森
小广告先森}

我要回帖

更多关于 unity3d 协程 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信