扩展Coroutine:自定义YieldInstruction

来源:互联网 发布:质量管理体系数据分析 编辑:程序博客网 时间:2024/06/06 03:30

点击打开链接


Unity的Coroutine机制无疑是这个引擎的一个亮点,可以把很多异步的逻辑用一种顺序的书写方式去实现,这个还是很重要的。

关于Coroutine的原理,说实话,这个没有源代码,不好说具体是怎么实现的。看他用到了IEnumerator,想象是使用了.Net的枚举机制。网上有不是探讨Coroutine实现原理的帖子,有兴趣的同学可以翻开看看。

Coroutine之所以强大,缺不了一系列YieldInstruction的派生类,包括:WaitForSeconds , WaitForFixedUpdate,AsyncOperation等等,我一直在琢磨能不能实现自己的YieldInstruction派生类呢?今天花时间尝试了一些,答案是“不能,也能”,哈哈。为什么说“不能”呢,是因为YieldInstruction这个类可没什么虚函数可以去override的,为什么说“能”呢!我们可以借用一个特殊的YieldInstruction派生类,来实现等同的功能,这个类就是Coroutine。

完整的演示工程下载:http://pan.baidu.com/s/1jGGhRKE


这个工程是这样一个例子:
假设我们想实现这样一个YieldInstruction:当一个动画播放完事儿之后,程序再继续。

具体的实现也很简单,首先我们需要一个IEnumerator的派生类,并实现其3个接口,具体代码如下:

[csharp] view plaincopy
  1. using UnityEngine;  
  2. using System.Collections;  
  3.   
  4. public class WaitForEndOfAnim : IEnumerator  
  5. {  
  6.     AnimationState m_animState;  
  7.   
  8.     public WaitForEndOfAnim(AnimationState animState)  
  9.     {  
  10.         m_animState = animState;  
  11.     }  
  12.     //-- IEnumerator Interface  
  13.     public object Current  
  14.     {  
  15.         get  
  16.         {  
  17.             return null;  
  18.         }  
  19.     }  
  20.   
  21.     //-- IEnumerator Interface  
  22.     public bool MoveNext()  
  23.     {  
  24.         return m_animState.enabled;  
  25.     }  
  26.   
  27.     //-- IEnumerator Interface  
  28.     public void Reset()  
  29.     {  
  30.     }  
  31. }  

这里面核心的逻辑就在“MoveNext”函数中,我通过m_animState.enabled来判断动画是否播放完了。

有了这个类时候,如果我们在协程函数体中写:yield return new WaitForEndOfAnim(animState),发现并没有其作用。后来改为:yield return StartCoroutine(new WaitForEndOfAnim(animAttack));就OK了。完整的测试代码如下:

[csharp] view plaincopy
  1. using UnityEngine;  
  2. using System.Collections;  
  3.   
  4. public class UnitTest : MonoBehaviour  
  5. {  
  6.   
  7.     // Use this for initialization  
  8.     void Start()  
  9.     {  
  10.     }  
  11.   
  12.     void OnGUI()  
  13.     {  
  14.         GUILayout.BeginArea(new Rect(6, 6, 200, 300));  
  15.         GUILayout.BeginVertical();  
  16.         GUILayout.Box("Conrountinue测试");  
  17.   
  18.         if (GUILayout.Button("启动"))  
  19.         {  
  20.             StartCoroutine(DoTest());  
  21.         }  
  22.   
  23.         GUILayout.EndVertical();  
  24.         GUILayout.EndArea();  
  25.     }  
  26.   
  27.     IEnumerator DoTest()  
  28.     {  
  29.   
  30.         Animation anim = GetComponentInChildren<Animation>();  
  31.         AnimationState animAttack = anim["attack"];  
  32.         animAttack.speed = 0.1f;  
  33.   
  34.         AnimationState animHit = anim["hit"];  
  35.         animHit.speed = 0.1f;  
  36.   
  37.         AnimationState animDie = anim["die"];  
  38.         animDie.speed = 0.1f;  
  39.   
  40.         Debug.Log("1.开始播放攻击动画。" + Time.time * 1000);  
  41.         anim.Play(animAttack.name);  
  42.         yield return StartCoroutine(new WaitForEndOfAnim(animAttack));  
  43.   
  44.         Debug.Log("2.开始播放受击动画。" + Time.time * 1000);  
  45.         anim.Play(animHit.name);  
  46.         yield return StartCoroutine(new WaitForEndOfAnim(animHit));  
  47.   
  48.         Debug.Log("3.开始播放死亡动画。" + Time.time * 1000);  
  49.         anim.Play(animDie.name);  
  50.         yield return StartCoroutine(new WaitForEndOfAnim(animDie));  
  51.   
  52.     }  
  53. }  
最后,运行结果看下图,注意时间戳:




0 0
原创粉丝点击