以前一直用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 了以后,下次调用的时机。
using UnityEngine;
using System.Collections;
public class CoroutineHelper : MonoBehaviour {
void Start () {
StartCoroutine (Fireworks());
}
void Update () {
Debug.Log("Update time = " + Time.time);
}
void LateUpdate () {
Debug.Log("LateUpdate time = " + Time.time);
}
bool bInTask = true;
IEnumerator Fireworks(){
Debug.Log ("outside while"); // 这里只是进来一次
while (bInTask) {
float timestart = Time.time;
Debug.Log ("inside while-1:" + timestart);
yield return null; // 下一帧调用, 什么都不做
Debug.Log ("inside while-2:" + timestart);
}
}
void OnGUI(){
if (GUILayout.Button ("stop")) {
bInTask = false;
}
}
}
这里不是在扣字眼,因为我从来不扣字眼,为啥要去测试这个玩意,是为了更好理解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的功能。
using UnityEngine;
using System.Collections;
public class Cortine : MonoBehaviour {
IEnumerator TestCorotine(){
float startTime = Time.time;
Debug.Log("startTime - 1: " + startTime);
yield return new WaitForSeconds(1.0f);
yield return StartCoroutine(new WaitTaskEndCoroutine(3.0f));
Debug.Log("endTime - 2: " + Time.time);
Debug.Log("endTime - 2: " + (Time.time - startTime));
}
void OnGUI(){
if(GUILayout.Button("TestCorotine")){
StartCoroutine(TestCorotine());
}
}
}
class WaitTaskEndCoroutine : IEnumerator{ //继承
float taskTime;
float startTime;
public WaitTaskEndCoroutine(float time){
taskTime = time;
startTime = Time.time;
}
public object Current{ //重写即可
get{
return null;
}
}
//这里我被黑了好久!~刚好把return 的值填反了~~一直没有执行下去
public bool MoveNext(){
if((Time.time - startTime)>=taskTime)
{
Debug.Log("end....");
return false;
}
Debug.Log("tick....");
return true;
}
public void Reset(){//重写即可
}}
我所看到的一些coroutine的优秀的写法
while(true)
{
dothing
yield return **
dothing
}
为啥说这个比较好的写法呢,可以想象一下如果放到update里面会出现什么呢? 首先这需求是一个我要反复做某件事,如果放到updae里面就真的是一帧执行一次了,这样写可以大大减少执行逻辑。