本文介绍几种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
26public 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版本简化了这种写法, 协程(枚举器)IEnumerator
的Current
对象是实现了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
50public 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压力。