Unity Coroutine的深度理解与实践

以前一直用unity的 coroutine,什么意思,以及怎么用都知道(我相信大部分人都知道,但是要说出其中的原理,以及如何来扩展自己的coroutine 就不是每个人都知道了)

其实了解一个事物基本上都是从几个方面来:第一,能干嘛,能解决什么问题;  第二,怎么用,能否自己扩展

下面不装B,直接进入正题:

coroutine 的存在其实是为了缓解update的压力,试想如果逻辑的检查都丢到update里面去,那么update将会不堪重负,而且有些特殊的情况根本就不适合丢到update里面去。

比如:角色死亡后20s复活,这样的需求,如果丢到 update里面去,那么每一帧都会去判断角色是否死亡这个很少的需求。而如果用coroutine那么不需要放到没帧去检查了,只需要你执行逻辑那里调用即可。(WaitForSeconds大家都会用)

说到这里,需要了解一下coroutine的生命周期,为啥这么说呢? 因为一度有好多人以为coroutine是thread的替代品(Lua就是这样),而unity其实coroutine并不是thread的替代品,它跟Start, Update 等函数一样,都是Unity底层维护并调用的,具体可以查看unity官方文档,这里只取最重要的部分


这里可以看到,coroutine的执行是在update之后,lateupdate和渲染之前。也就是说当你yield 了以后,下次调用的时机。


[csharp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. using UnityEngine;  
  2. using System.Collections;  
  3.   
  4. public class CoroutineHelper : MonoBehaviour {  
  5.   
  6.     void Start () {  
  7.         StartCoroutine (Fireworks());  
  8.     }  
  9.   
  10.     void Update () {  
  11.         Debug.Log("Update time = " + Time.time);  
  12.     }  
  13.   
  14.     void LateUpdate () {  
  15.         Debug.Log("LateUpdate time = " + Time.time);  
  16.     }  
  17.   
  18.     bool bInTask = true;  
  19.     IEnumerator Fireworks(){  
  20.         Debug.Log ("outside while"); // 这里只是进来一次  
  21.         while (bInTask) {   
  22.             float timestart = Time.time;  
  23.             Debug.Log ("inside while-1:" + timestart);  
  24.             yield return null// 下一帧调用, 什么都不做  
  25.             Debug.Log ("inside while-2:" + timestart);  
  26.         }  
  27.     }  
  28.         
  29.     void OnGUI(){  
  30.         if (GUILayout.Button ("stop")) {  
  31.             bInTask = false;  
  32.         }  
  33.     }  
  34. }  

这里不是在扣字眼,因为我从来不扣字眼,为啥要去测试这个玩意,是为了更好理解coroutine的执行顺序,然后管理游戏,试想一下相机的处理逻辑一般我们会放到lateupate中,因为这个时候逻辑更新已经处理,咳咳,所以coroutine其实也已经处理了,所以不用担心在coroutine里面的东西有没有在相机之前被执行。

其实从流程图里面可以看到还有好多形式的return 类型。

NULL  : 等待一帧进行处理后续逻辑

WaitForSeconds: 等待指定的时间后处理后续逻辑

WWW:等待下载完毕再处理后续逻辑

Coroutine: 等待着coroutine结束在处理后续逻辑


如何停止coroutine呢? 

这个问题就直接给答案了,一个是通过unity的 stop*** ,另外一个就是把GameObject给enable =false 掉,如果你enable掉的是GameObject上的脚本,你将会发现coroutine并没有被关掉。这里也可以说明coroutine和mono的执行还是不同的,虽然都是由底层来调用。但是和所在的mono没啥关系,只是有所在的mono进行启动而已



好吧~上面的可能并不过瘾~你想要做自己的 干完****处理后续逻辑。这里就需要扩展了~~~

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

扩展部分:

这里有两篇文章,写得不错,先给你们看看 http://www.2cto.com/kf/201507/415345.html   http://www.tuicool.com/articles/ArQJB3

其实很多事都是这样,看上去不起眼的东西当你去实现就会遇到坑哈哈,我就遇到了,当然是我自己sb了,希望能对其他同学有所帮助,我这里模拟了WaitForSeconds的功能。

[csharp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. using UnityEngine;  
  2. using System.Collections;  
  3.   
  4. public class Cortine : MonoBehaviour {  
  5.   
  6.     IEnumerator TestCorotine(){  
  7.         float startTime = Time.time;  
  8.         Debug.Log("startTime - 1: " + startTime);  
  9.         yield return new WaitForSeconds(1.0f);  
  10.         yield return StartCoroutine(new WaitTaskEndCoroutine(3.0f));  
  11.         Debug.Log("endTime - 2: " + Time.time);  
  12.         Debug.Log("endTime - 2: " + (Time.time - startTime));  
  13.     }  
  14.   
  15.     void OnGUI(){  
  16.         if(GUILayout.Button("TestCorotine")){  
  17.             StartCoroutine(TestCorotine());  
  18.         }  
  19.     }  
  20. }  
  21.   
  22. class WaitTaskEndCoroutine : IEnumerator{ //继承   
  23.     float taskTime;  
  24.     float startTime;  
  25.     public WaitTaskEndCoroutine(float time){  
  26.         taskTime = time;  
  27.         startTime = Time.time;  
  28.     }  
  29.    
  30.     public object Current{  //重写即可  
  31.         get{  
  32.             return null;  
  33.         }  
  34.     }  
  35.     //这里我被黑了好久!~刚好把return 的值填反了~~一直没有执行下去  
  36.     public bool MoveNext(){  
  37.         if((Time.time - startTime)>=taskTime)  
  38.         {  
  39.             Debug.Log("end....");  
  40.             return false;  
  41.         }  
  42.         Debug.Log("tick....");  
  43.         return true;  
  44.     }  
  45.   
  46.     public void Reset(){<pre name="code" class="csharp">//重写即可  
}}

 

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

我所看到的一些coroutine的优秀的写法

while(true)

{

      dothing

      yield return **

     dothing

}

为啥说这个比较好的写法呢,可以想象一下如果放到update里面会出现什么呢? 首先这需求是一个我要反复做某件事,如果放到updae里面就真的是一帧执行一次了,这样写可以大大减少执行逻辑。

以前一直用unity的 coroutine,什么意思,以及怎么用都知道(我相信大部分人都知道,但是要说出其中的原理,以及如何来扩展自己的coroutine 就不是每个人都知道了)

其实了解一个事物基本上都是从几个方面来:第一,能干嘛,能解决什么问题;  第二,怎么用,能否自己扩展

下面不装B,直接进入正题:

coroutine 的存在其实是为了缓解update的压力,试想如果逻辑的检查都丢到update里面去,那么update将会不堪重负,而且有些特殊的情况根本就不适合丢到update里面去。

比如:角色死亡后20s复活,这样的需求,如果丢到 update里面去,那么每一帧都会去判断角色是否死亡这个很少的需求。而如果用coroutine那么不需要放到没帧去检查了,只需要你执行逻辑那里调用即可。(WaitForSeconds大家都会用)

说到这里,需要了解一下coroutine的生命周期,为啥这么说呢? 因为一度有好多人以为coroutine是thread的替代品(Lua就是这样),而unity其实coroutine并不是thread的替代品,它跟Start, Update 等函数一样,都是Unity底层维护并调用的,具体可以查看unity官方文档,这里只取最重要的部分

这里可以看到,coroutine的执行是在update之后,lateupdate和渲染之前。也就是说当你yield 了以后,下次调用的时机。

[csharp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. using UnityEngine;  
  2. using System.Collections;  
  3.   
  4. public class CoroutineHelper : MonoBehaviour {  
  5.   
  6.     void Start () {  
  7.         StartCoroutine (Fireworks());  
  8.     }  
  9.   
  10.     void Update () {  
  11.         Debug.Log("Update time = " + Time.time);  
  12.     }  
  13.   
  14.     void LateUpdate () {  
  15.         Debug.Log("LateUpdate time = " + Time.time);  
  16.     }  
  17.   
  18.     bool bInTask = true;  
  19.     IEnumerator Fireworks(){  
  20.         Debug.Log ("outside while"); // 这里只是进来一次  
  21.         while (bInTask) {   
  22.             float timestart = Time.time;  
  23.             Debug.Log ("inside while-1:" + timestart);  
  24.             yield return null// 下一帧调用, 什么都不做  
  25.             Debug.Log ("inside while-2:" + timestart);  
  26.         }  
  27.     }  
  28.         
  29.     void OnGUI(){  
  30.         if (GUILayout.Button ("stop")) {  
  31.             bInTask = false;  
  32.         }  
  33.     }  
  34. }  

这里不是在扣字眼,因为我从来不扣字眼,为啥要去测试这个玩意,是为了更好理解coroutine的执行顺序,然后管理游戏,试想一下相机的处理逻辑一般我们会放到lateupate中,因为这个时候逻辑更新已经处理,咳咳,所以coroutine其实也已经处理了,所以不用担心在coroutine里面的东西有没有在相机之前被执行。

其实从流程图里面可以看到还有好多形式的return 类型。

NULL  : 等待一帧进行处理后续逻辑

WaitForSeconds: 等待指定的时间后处理后续逻辑

WWW:等待下载完毕再处理后续逻辑

Coroutine: 等待着coroutine结束在处理后续逻辑

如何停止coroutine呢? 

这个问题就直接给答案了,一个是通过unity的 stop*** ,另外一个就是把GameObject给enable =false 掉,如果你enable掉的是GameObject上的脚本,你将会发现coroutine并没有被关掉。这里也可以说明coroutine和mono的执行还是不同的,虽然都是由底层来调用。但是和所在的mono没啥关系,只是有所在的mono进行启动而已

好吧~上面的可能并不过瘾~你想要做自己的 干完****处理后续逻辑。这里就需要扩展了~~~

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

扩展部分:

这里有两篇文章,写得不错,先给你们看看 http://www.2cto.com/kf/201507/415345.html   http://www.tuicool.com/articles/ArQJB3

其实很多事都是这样,看上去不起眼的东西当你去实现就会遇到坑哈哈,我就遇到了,当然是我自己sb了,希望能对其他同学有所帮助,我这里模拟了WaitForSeconds的功能。

[csharp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. using UnityEngine;  
  2. using System.Collections;  
  3.   
  4. public class Cortine : MonoBehaviour {  
  5.   
  6.     IEnumerator TestCorotine(){  
  7.         float startTime = Time.time;  
  8.         Debug.Log("startTime - 1: " + startTime);  
  9.         yield return new WaitForSeconds(1.0f);  
  10.         yield return StartCoroutine(new WaitTaskEndCoroutine(3.0f));  
  11.         Debug.Log("endTime - 2: " + Time.time);  
  12.         Debug.Log("endTime - 2: " + (Time.time - startTime));  
  13.     }  
  14.   
  15.     void OnGUI(){  
  16.         if(GUILayout.Button("TestCorotine")){  
  17.             StartCoroutine(TestCorotine());  
  18.         }  
  19.     }  
  20. }  
  21.   
  22. class WaitTaskEndCoroutine : IEnumerator{ //继承   
  23.     float taskTime;  
  24.     float startTime;  
  25.     public WaitTaskEndCoroutine(float time){  
  26.         taskTime = time;  
  27.         startTime = Time.time;  
  28.     }  
  29.    
  30.     public object Current{  //重写即可  
  31.         get{  
  32.             return null;  
  33.         }  
  34.     }  
  35.     //这里我被黑了好久!~刚好把return 的值填反了~~一直没有执行下去  
  36.     public bool MoveNext(){  
  37.         if((Time.time - startTime)>=taskTime)  
  38.         {  
  39.             Debug.Log("end....");  
  40.             return false;  
  41.         }  
  42.         Debug.Log("tick....");  
  43.         return true;  
  44.     }  
  45.   
  46.     public void Reset(){<pre name="code" class="csharp">//重写即可  
}}

 

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

我所看到的一些coroutine的优秀的写法

while(true)

{

      dothing

      yield return **

     dothing

}

为啥说这个比较好的写法呢,可以想象一下如果放到update里面会出现什么呢? 首先这需求是一个我要反复做某件事,如果放到updae里面就真的是一帧执行一次了,这样写可以大大减少执行逻辑。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值