原文:http://blog.csdn.net/tkokof1/article/details/11842673


一 引子

 使用Unity已经有一段时间了,对于Component、GameObject之类的概念也算是有所了解,而脚本方面从一开始就选定了C#,目前来看还 是挺明智的:Boo太小众,而且支持有限;JS(或着说UnityScript)的话稍稍自由散漫了些,不太符合我们这些略显严谨的程序猿;相比之 下,C#各方面都十分沁人心腑,使用起来还是相当舒畅的 :)

 就游戏开发而言,Unity也确实为我们减轻了不少开发负担、缩短了很多开发流程,但从开发原理上来讲,使用Unity你仍然避不开许多传统的开发技术, 譬如几乎所有游戏程序都有的Update,在Unity里就变成了MonoBehaviour的一个成员方法;而另一个几乎与Update并重的Init 方法,在Unity里则被换成了Start。可以这么说,Unity虽然极大的简化了游戏开发流程,但从方法原理上来讲的话,其实他也并没有和传统开发方 式存在非常大的差异,Update还是那个Update,Init还是那个Init,只不过换了一个更简单的形式而已~

 依此思路,我持续着自己的Unity学习之路,也逐步验证着自己上述的观点,直到有一天,我遇到了Coroutine ……

 二. Coroutine是什么?

 延时大概是游戏编程中最司空见惯的需求之一:角色移动控制需要延时、事件触发需要延时、甚至开启一个粒子特效有时也需要延时,可以说,延时在游戏开发中几 乎无处不在 :)有鉴于此,很多的游戏引擎对于延时控制都提供了很好的支持,譬如在cocos2d-x中,CCDelayTime就是专门用来干这个的,当然,其他引 擎也有自己不同的支持方式,但是从实现层面来讲,基本都是“标记开始时间,Update中持续更新检查”这种方法,从代码上来看,大抵是这么一个样子:

 float delayTime =

simple coroutine manager class
//

using UnityEngine;
using System.Collections.Generic;

public class CoroutineManager : MonoBehaviour {

public static CoroutineManager Instance {
   get;
private set;
}

List m_enumerators = new List();
List m_enumeratorsBuffer = new List();

void Awake() {
   if (Instance == null) {
   Instance = this;
}
else {
   Debug.LogError("Multi-instances of CoroutineManager");
}
}

void LateUpdate() {
   for (int i = 0; i < m_enumerators.Count; ++i) {
// handle special enumerator
if (m_enumerators[i].Current is CoroutineYieldInstruction) {
   CoroutineYieldInstruction yieldInstruction = m_enumerators[i].Current as CoroutineYieldInstruction;
if (!yieldInstruction.IsDone()) {
   continue;
}
}
// other special enumerator here ...

// do normal move next
if (!m_enumerators[i].MoveNext()) {
   m_enumeratorsBuffer.Add(m_enumerators[i]);
continue;
}
}

// remove end enumerator
for (int i = 0; i < m_enumeratorsBuffer.Count; ++i) {
   m_enumerators.Remove(m_enumeratorsBuffer[i]);
}
m_enumeratorsBuffer.Clear();
}

public void StartCoroutineSimple(System.Collections.IEnumerator enumerator) {
m_enumerators.Add(enumerator);
}

}

接着便是我们自己的WaitForSeconds了,不过在此之前我们先来实现WaitForSeconds的基类,CoroutineYieldInstruction:

//
//    Hugo
//    coroutine yield instruction base class
//

using UnityEngine;
using System.Collections;

public class CoroutineYieldInstruction {

public virtual bool IsDone() {
   return true;
}

}

 很简单不是吗?类型仅有一个虚拟的IsDone方法,上面的CoroutineManager就是依据此来进行迭代器迭代的,OK,该是我们的WaitForSeconds上场了:

//
//    Hugo
//    coroutine wait for seconds class
//

using UnityEngine;
using System.Collections;

public class CoroutineWaitForSeconds : CoroutineYieldInstruction {

float m_waitTime;
float m_startTime;

public CoroutineWaitForSeconds(float waitTime) {
m_waitTime = waitTime;
m_startTime = -1;
}

public override bool IsDone() {
// NOTE: a little tricky here
if (m_startTime < 0) {
   m_startTime = Time.time;
}

// check elapsed time
return (Time.time - m_startTime) >= m_waitTime;
}

}

原理非常简单,每次IsDone调用时进行累时,直到延时结束,就这么简单 :)

写个简单的案例来测试一下:

//
//    Hugo
//    coroutine test case
//

using UnityEngine;
using System.Collections;

public class CoroutineTest: MonoBehaviour {

void Start() {
// start unity coroutine
StartCoroutine(UnityCoroutine());
   // start self coroutine
CoroutineManager.Instance.StartCoroutineSimple(SelfCoroutine());
}

IEnumerator UnityCoroutine() {
Debug.Log("Unity coroutine begin at time : " + Time.time);
yield return new WaitForSeconds(5);
Debug.Log("Unity coroutine begin at time : " + Time.time);
}

IEnumerator SelfCoroutine() {
Debug.Log("Self coroutine begin at time : " + Time.time);
yield return new CoroutineWaitForSeconds(5);
Debug.Log("Self coroutine begin at time : " + Time.time);
}

}

效果虽然不如原生的WaitForSeconds那么精确,但也基本符合期望,简单给张截图:




四 尾声

 Coroutine这个东西对于我来说确实比较陌生,其中的迭代原理也困扰了我许久,不少抵触情绪也“油然而生”(在此自我反省一下),但是经过简单的一 阵子试用,我却赫然发现自己竟然离不开他了!究其原因,可能是其简洁高效的特性深深折服了我,想想以前那些个分散于代码各处的计时变量和事件逻辑,现在统 统都可以做成一个个Coroutine,不仅易于理解而且十分高效,我相信不管是谁,在实际使用了Unity中的Coroutine之后,都会对他爱不释 手的~ :)当然,这么好的东西网上自然早以有了非常优秀的介绍,有兴趣的朋友可以仔细看看 :)

 好了,就这样吧,下次再见了~