Unity里的Coroutine在帮助我们实现序列化事件时尤其方便。可以让事件依次发生,可以让事件A等待事件B结束后才开始执行。
但需要厘清几个基本概念。
Coroutines不是多线程,不是异步技术。Coroutines都在MainThread中执行,且每个时刻只有一个Coroutine在执行。 Coroutine是一个function,可以部分地执行,当条件满足时,未来会被再次执行直到整个函数执行完毕。
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. (http://unitygems.com/coroutines/)
Unity在每一帧都会去处理GameObject里带有的Coroutine Function,直到CoroutineFunciton被执行完毕。
当一个Coroutine开始启动时,它会执行到遇到yield为止,遇到yieldCoroutine会暂停执行,直到满足yield语句的条件,会开始执行yield语句后面的内容,直到遇到下一个yield为止…… 如此循环,直到整个函数结束。
将函数分到多个帧里去执行,帅嘛!
http://unitygems.com/coroutines/ 这个网址里关于Coroutines的教程很好,强烈推荐好好读读。
一个Coroutines应用的小例子,动画播放到某个指定进度执行某些事情:
using UnityEngine;
using System.Collections;
public class DieSequence : MonoBehaviour {
// Use this for initialization
void Start () {
StartCoroutine(Die ());
}
//Wait for an animation to be a certain amount complete
IEnumerator WaitForAnimation(string name, float ratio, bool play)
{
//Get the animation state for the named animation
var anim = animation[name];
//Play the animation
if(play)
{
animation.Play(name);
animation[name].speed = 0.3f;
}
//Loop until the normalized time reports a value
//greater than our ratio. This method of waiting for
//an animation accounts for the speed fluctuating as the
//animation is played.
while(anim.normalizedTime + float.Epsilon + Time.deltaTime < ratio)
yield return new WaitForEndOfFrame();
}
IEnumerator Die()
{
//Wait for the die animation to be 50% complete
yield return StartCoroutine(WaitForAnimation("anim_die005_360",0.5f, true));
//Drop the enemies on dying pickup. Do sth you need
Debug.Log("Half Death, Drop sth");
yield return StartCoroutine(WaitForAnimation("anim_die005_360",1f, false));
Debug.Log("Whole Anim Ended.");
Destroy(gameObject);
}
}
Coroutine里面套Coroutine: StartCoroutine(Die()), Die()里StartCoroutine(WaitForAnimation("anim_die005_360",0.5f, true)),
当Die()里WaitForAnimation的Coroutine完成后(即Animation的播放进度大于ratio了),yield return了,才能在接下来的Frame里,执行Die()后面的东东。
由于WaitForAnimation()里并没有PauseAnimation或者StopAnimation,所以其实Die()函数里没有 yield return StartCoroutine(WaitForAnimation("anim_die005_360",1f, false));这一行,动画也会播放完毕。这一句可以保证在动画播放完毕时去执行某些东东。
使用WaitForSeconds(float time)时需要注意,当Time.timeScale = 0f时,yield return new WaitForSeconds(t) (t>0)永远不会返回,即这行后面的rountine不会被执行到。
如果在游戏里游戏时间静止时,又想用WaitForSeconds类似的功能怎么办?
可以自己实现类似的功能。
例如:
IEnumerator CutSequence()
{
_runningSequence = true;
Time.timeScale = 0;
var originalPosition = Camera.main.transform.position;
foreach(var ball in BallScript.allBalls.ToArray())
{
if (ball != null)
{
Vector3 targetPosition = ball.transform.position - Vector3.forward * 2f;
yield return StartCoroutine(MoveObject(Camera.main.transform, targetPosition, 2));
yield return WaitForRealSeconds(0.5f);
}
}
Coroutine WaitForRealSeconds(float time)
{
return StartCoroutine(Wait(time));
}
IEnumerator Wait(float time)
{
var current = Time.realtimeSinceStartup;
while(Time.realtimeSinceStartup - current < time)
{
yield return null;
}
}
Unity里的协程Coroutines