Unity 5.3版本中协程自定义yield语句

本文介绍几种Unity协程中自定义yield语句的方法,并对比其在5.3版本和旧版本的中实现的区别。

引言

Unity中Coroutine非常好用,但是5.3之前的版本不能优雅的实现自定义yield语句。Unity5.3中引入了CustomYieldInstruction类并改变了一些内部yield 语句规则,让我们优雅的实现自定义yield语句。

继承CustomYieldInstruction

5.3引入了新的类CustomYieldInstruction使开发者可以自定义yield语句。通过继承CustomYieldInstruction override keepWaiting 的值决定是否暂停该协程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class CustomWaitForSeconds : CustomYieldInstruction
{
private float until;
// 上层协程(枚举器)是否继续等待
public override bool keepWaiting
{
get
{
return Time.unscaledTime < until;
}
}

public CustomWaitForSeconds(float seconds)
{

until = Time.unscaledTime + seconds;
}
}

public class WaitForSecondsTest : MonoBehaviour
{
IEnumerator Start()
{

yield return new CustomWaitForSeconds(5);
Debug.Log("5 seconds elapsed...");
}
}

IEnumerator接口

我们也可以通过实现IEnumerator接口来实现自定义yield语句。接口MoveNext()返回布尔值觉得决定上层协程(枚举器)是否继续等待。但是之前的版本中代码中要这样yield return StartCoroutine(enumerator);, 手动启动一个子协程,等待这个子协程执行完毕。5.3版本简化了这种写法, 协程(枚举器)IEnumeratorCurrent对象是实现了IEnumerator接口的话,则会等待这个子协程对象遍历完毕后才继续执行(文档)。 也就是父协程(枚举器)会等待Current子协程(枚举器)执行完毕(Current对象实现了IEnumerator)才会继续执行。 所以新版本中只需要yield return enumerator;就可以了, Unity内部会自动开启子协程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class WaitForSecondsUnscaled : IEnumerator
{
private float until;

public object Current { get { return null; } }

public WaitForSecondsUnscaled(float seconds)
{

seconds = Mathf.Max(seconds, 0);
until = Time.unscaledTime + seconds;
}
// 返回为true则父协程暂停,等待该协程继续执行;返回为false父协程继续执行
public bool MoveNext()
{

return Time.unscaledTime < until;
}

public void Reset()
{

until = 0;
}
}

public class WaitForSecondsTest : MonoBehaviour
{
IEnumerator Start()
{

yield return new CustomWaitForSeconds(5);
Debug.Log("5 seconds elapsed...");
// 新方式
yield return new WaitForSecondsUnscaled(5);
// 旧方式
//yield return StartCoroutine(new WaitForSecondsUnscaled(5));
Debug.Log("10 seconds elapsed...");
// 新方式
yield return WaitUntilUnscaled(Time.unscaledTime + 5);
// 旧方式
//yield return StartCoroutine(WaitUntilUnscaled(Time.unscaledTime + 5));
Debug.Log("15 seconds elapsed...");
}

// 返回IEnumerator接口实例的函数
IEnumerator WaitUntilUnscaled(float until)
{

while (Time.unscaledTime < until)
{
yield return null;
}
}
}

结语

上面两个例子可以看出5.3版本中的自定义yield语句非常好用,对比旧版本更清晰更直观。请记住协程好用但是不能滥用,用好了协程会使逻辑更清晰,滥用容易造成GC压力。