当你调用一个函数时,它会在返回之前运行完成。这实际上意味着函数中发生的任何动作都必须在单帧更新中发生;函数调用不能用于包含程序动画或一段时间内的事件序列。例如,考虑逐渐降低对象的 alpha(不透明度)值直到它变得完全不可见的任务。
void Fade()
{
for (float ft = 1f; ft >= 0; ft -= 0.1f)
{
Color c = renderer.material.color;
c.a = ft;
renderer.material.color = c;
}
}
就目前而言,淡入淡出功能不会产生您所期望的效果。为了使渐变可见,必须在一系列帧上减少 alpha 以显示正在渲染的中间值。但是,该函数将在单个帧更新中完整执行。中间值永远不会被看到,对象会立即消失。
可以通过将代码添加到逐帧执行淡入淡出的 Update 函数来处理此类情况。但是,使用协程来完成此类任务通常更方便。
协程就像一个函数,它能够暂停执行并将控制权返回给 Unity,然后在下一帧从中断处继续。在 C# 中,协程是这样声明的:
IEnumerator Fade()
{
for (float ft = 1f; ft >= 0; ft -= 0.1f)
{
Color c = renderer.material.color;
c.a = ft;
renderer.material.color = c;
yield return null;
}
}
它本质上是一个函数,其返回类型为 IEnumerator,并且在函数体的某处包含了 yield return 语句。 yield return null 行是执行将暂停并在下一帧恢复的点。设置协程运行,需要使用 StartCoroutine 函数:
void Update()
{
if (Input.GetKeyDown("f"))
{
StartCoroutine("Fade");
}
}
您会注意到 Fade 函数中的循环计数器在协程的生命周期内保持其正确值。事实上,任何变量或参数都将在产量之间正确保留。
默认情况下,协程在产生后会在帧上恢复,但也可以使用 WaitForSeconds 引入时间延迟:
IEnumerator Fade()
{
for (float ft = 1f; ft >= 0; ft -= 0.1f)
{
Color c = renderer.material.color;
c.a = ft;
renderer.material.color = c;
yield return new WaitForSeconds(.1f);
}
}
这可以用作在一段时间内传播效果的一种方式,但它也是一种有用的优化。游戏中的许多任务需要定期执行,最明显的方法是将它们包含在更新功能中。但是,此函数通常每秒会被调用多次。当一项任务不需要如此频繁地重复时,您可以将其放入协程中以定期获取更新,但不是每一帧。这方面的一个例子可能是一个警报,如果敌人在附近,它会警告玩家。代码可能如下所示:
bool ProximityCheck()
{
for (int i = 0; i < enemies.Length; i++)
{
if (Vector3.Distance(transform.position, enemies[i].transform.position) < dangerDistance) {
return true;
}
}
return false;
}
如果有很多敌人,那么每帧调用这个函数可能会带来很大的开销。但是,您可以使用协程每隔十分之一秒调用一次:
IEnumerator DoCheck()
{
for(;;)
{
ProximityCheck();
yield return new WaitForSeconds(.1f);
}
}
这将大大减少执行的检查次数,而不会对游戏玩法产生任何明显影响。
注意:您可以使用 StopCoroutine 和 StopAllCoroutines 停止协程。当使用 SetActive(false) 禁用它所附加的 GameObject 时,协程也会停止。调用 Destroy(example)(其中 example 是 MonoBehaviour 实例)会立即触发 OnDisable 并处理协程,从而有效地停止它。最后,在帧结束时调用 OnDestroy。
通过在 MonoBehaviour 实例上将 enabled 设置为 false 来禁用 MonoBehaviour 时,协程不会停止。
参考:https://docs.unity3d.com/Manual/Coroutines.html