在 Unity 中,我什么时候应该使用协程而不是在 Update() 中减去 Time.deltaTime?
In Unity, when should I use coroutines versus subtracting Time.deltaTime in Update()?
下面是我想强调的差异的简单示例。
使用协程:
public float repeatRate = 5f;
void Start()
{
StartCoroutine("RepeatSomething");
}
IEnumerator RepeatSomething()
{
while (true)
{
yield return new WaitForSeconds(repeatRate);
// Do something
}
}
使用 Update()
和 Time.deltaTime
:
public float repeatRate = 5f;
private float timer = 0;
void Update()
{
if (timer < 0)
{
// Do something
timer = repeatRate;
}
timer -= Time.deltaTime;
}
我什么时候应该使用一个而不是另一个,每个 advantages/disadvantages 是什么?
简单的答案是使用更易读的那个(据我所知,性能没有明显差异)。我会(通常)使用协同程序,因为这将有助于使您的更新循环更有条理,而且我觉得它们通常更容易设置(您不需要为计时器等创建变量)
老实说,他们将执行相同的任务。它们都将执行每一帧(在您的情况下,检查自初始执行以来是否已经过了 5 秒)。但是,存在一些 关键差异 。即,最重要的一个,是执行顺序。 Coroutines
总是 运行 在 Update()
之后。如果出于某种原因,Update()
中发生的某些事情需要刷新,以便使用正确的信息更新 Coroutine
,那么这很重要。但通常情况下,情况并非如此。
总而言之,任何一个都能完成您想要的任务,通常只是个人喜好和组织问题。
对于每一个 yield,您实际上是在创建一个新对象,正如您所说 new WaitForSeconds()
。但是,差异仍然没有那么显着。另一方面,协程更具可读性,特别是考虑到你需要在同一个协程中使用多个等待时间不同的 yield 的情况,在 Update 中实现这样的事情会使你的代码非常混乱。
永远不要使用协程。
这将极大地改善您解决问题的方式,尤其是在游戏引擎中,并避免不必要的垃圾创建、垃圾收集和缓慢的调用以及与接口和协程相关的其他问题。
这也意味着您的编程速度更快、更简单,并且更容易思考和推理什么在做什么、在什么地方、什么时候、为什么以及如何做。
协程是一种令人上瘾的药物,应该避免,就好像它们没有任何好处一样。因为他们没有比更好地思考你的程序和更好地编写它的好处。
一旦开始使用协程,就有点像在咖啡中加入奶油和糖。你总是会添加更多的每一个,因为它们有点结合在一起,这一切似乎都在增强东西。
你最终甚至会在咖啡质量上妥协,因为它的味道似乎不再那么重要。您已经对乳制品、糖和咖啡上瘾,而不仅仅是咖啡因。
你会开始喝太多,因为现在你已经掌握了食谱,所以很容易喝下去。味道很正,不会错的。但是你会焦躁不安,压力大,睡不好,身上会长出零星的脂肪,在你最爱的人面前你会暴躁。
看似解决的问题变成了问题。然后真正的大问题开始出现,在您的健康和心理健康方面。
所有这一切都可以通过简单地喝绿茶而不使用协程来避免。
因为你后来为成为咖啡瘾君子而付出的代价根本不值得。
永远。
在大多数情况下,答案是。
一般来说Update和Coroutine的性能差异是无关紧要的。只需遵循最适合您的方法,但如果您想遵循类似协程的方法,请使用性能更高的 MEC Coroutines
而不是 Unity Coroutine
。
MEC Coroutine
performance analysis。
Unity Coroutine
performance analysis.
简单用法
如unity forum中提到的一般“Coroutines
对于在多个帧上执行方法很有用[然后忘记它]。”
如果您打算只使用其中的几个(少于 10k?),那么您也可以使用 Unity Coroutines
。
高级用法
目前 Unity 支持 Task/Async,但性能仍然很低。因此,您可能会考虑使用 Coroutines
来模拟 Async
功能。
在这种情况下,您甚至可以使用 Coroutines
删除所有(或大部分)Update
循环,例如您发布的示例。
这在性能方面很有用,尤其是在您的特定情况下,因为当您使用 Update
循环时,即使您什么都不做,您也会不断地检查 if (timer < 0)
。甚至一个空的更新循环仍然是一个性能问题。
另一方面,您可能只是在事件发生时开始和停止 Coroutines
。当协程停止时,性能成本变为 0。
如果您想遵循此方法,我强烈建议使用 MEC coroutines,它可以消除由 Unity Coroutines 引起的所有性能问题(并具有相同的功能)。
结论
- 在大多数情况下,这种性能差异无关紧要
MEC Coroutines
比更新循环 性能更高
Update
循环通常比 Unity Coroutines
更高效
Unity Coroutines
应该只用于很少发生的简单定时任务
PS: 关于 unity coroutine 可能会帮助你深入了解它们是如何工作的。
PPS:this old answer 可能会让您有更深入的了解,但它有点过时,尤其是在谈到垃圾回收时。
只是想 post 在这里,我最近尝试了 MEC 协程,如果我们检查延迟和响应时间,它比 Unity 协程的性能更高,但只有当你 运行 这么多(比如至少超过 10K 个简单的),很可能你永远不会或不应该。然而,GC 与 Unity 的协程的工作从那时起就被修复了,实际上 MEC 协程现在给 GC 的工作更多了,大约是 10 倍!所以我用协程增加了一个数字,给一个新的协程一个增量,就像开发人员在 2016 年进行的性能测试一样,Unity 的协程支持 Playmode,但在我的案例中产生了 68MB 的垃圾,而 MEC 的则有0,73GB....
所以我将协程改回了 Unity 协程。
下面是我想强调的差异的简单示例。
使用协程:
public float repeatRate = 5f;
void Start()
{
StartCoroutine("RepeatSomething");
}
IEnumerator RepeatSomething()
{
while (true)
{
yield return new WaitForSeconds(repeatRate);
// Do something
}
}
使用 Update()
和 Time.deltaTime
:
public float repeatRate = 5f;
private float timer = 0;
void Update()
{
if (timer < 0)
{
// Do something
timer = repeatRate;
}
timer -= Time.deltaTime;
}
我什么时候应该使用一个而不是另一个,每个 advantages/disadvantages 是什么?
简单的答案是使用更易读的那个(据我所知,性能没有明显差异)。我会(通常)使用协同程序,因为这将有助于使您的更新循环更有条理,而且我觉得它们通常更容易设置(您不需要为计时器等创建变量)
老实说,他们将执行相同的任务。它们都将执行每一帧(在您的情况下,检查自初始执行以来是否已经过了 5 秒)。但是,存在一些 关键差异 。即,最重要的一个,是执行顺序。 Coroutines
总是 运行 在 Update()
之后。如果出于某种原因,Update()
中发生的某些事情需要刷新,以便使用正确的信息更新 Coroutine
,那么这很重要。但通常情况下,情况并非如此。
总而言之,任何一个都能完成您想要的任务,通常只是个人喜好和组织问题。
对于每一个 yield,您实际上是在创建一个新对象,正如您所说 new WaitForSeconds()
。但是,差异仍然没有那么显着。另一方面,协程更具可读性,特别是考虑到你需要在同一个协程中使用多个等待时间不同的 yield 的情况,在 Update 中实现这样的事情会使你的代码非常混乱。
永远不要使用协程。
这将极大地改善您解决问题的方式,尤其是在游戏引擎中,并避免不必要的垃圾创建、垃圾收集和缓慢的调用以及与接口和协程相关的其他问题。
这也意味着您的编程速度更快、更简单,并且更容易思考和推理什么在做什么、在什么地方、什么时候、为什么以及如何做。
协程是一种令人上瘾的药物,应该避免,就好像它们没有任何好处一样。因为他们没有比更好地思考你的程序和更好地编写它的好处。
一旦开始使用协程,就有点像在咖啡中加入奶油和糖。你总是会添加更多的每一个,因为它们有点结合在一起,这一切似乎都在增强东西。
你最终甚至会在咖啡质量上妥协,因为它的味道似乎不再那么重要。您已经对乳制品、糖和咖啡上瘾,而不仅仅是咖啡因。
你会开始喝太多,因为现在你已经掌握了食谱,所以很容易喝下去。味道很正,不会错的。但是你会焦躁不安,压力大,睡不好,身上会长出零星的脂肪,在你最爱的人面前你会暴躁。
看似解决的问题变成了问题。然后真正的大问题开始出现,在您的健康和心理健康方面。
所有这一切都可以通过简单地喝绿茶而不使用协程来避免。 因为你后来为成为咖啡瘾君子而付出的代价根本不值得。
永远。
在大多数情况下,答案是。
一般来说Update和Coroutine的性能差异是无关紧要的。只需遵循最适合您的方法,但如果您想遵循类似协程的方法,请使用性能更高的 MEC Coroutines
而不是 Unity Coroutine
。
MEC Coroutine
performance analysis。Unity Coroutine
performance analysis.
简单用法
如unity forum中提到的一般“Coroutines
对于在多个帧上执行方法很有用[然后忘记它]。”
如果您打算只使用其中的几个(少于 10k?),那么您也可以使用 Unity Coroutines
。
高级用法
目前 Unity 支持 Task/Async,但性能仍然很低。因此,您可能会考虑使用 Coroutines
来模拟 Async
功能。
在这种情况下,您甚至可以使用 Coroutines
删除所有(或大部分)Update
循环,例如您发布的示例。
这在性能方面很有用,尤其是在您的特定情况下,因为当您使用 Update
循环时,即使您什么都不做,您也会不断地检查 if (timer < 0)
。甚至一个空的更新循环仍然是一个性能问题。
另一方面,您可能只是在事件发生时开始和停止 Coroutines
。当协程停止时,性能成本变为 0。
如果您想遵循此方法,我强烈建议使用 MEC coroutines,它可以消除由 Unity Coroutines 引起的所有性能问题(并具有相同的功能)。
结论
- 在大多数情况下,这种性能差异无关紧要
MEC Coroutines
比更新循环 性能更高
Update
循环通常比Unity Coroutines
更高效
Unity Coroutines
应该只用于很少发生的简单定时任务
PS:
PPS:this old answer 可能会让您有更深入的了解,但它有点过时,尤其是在谈到垃圾回收时。
只是想 post 在这里,我最近尝试了 MEC 协程,如果我们检查延迟和响应时间,它比 Unity 协程的性能更高,但只有当你 运行 这么多(比如至少超过 10K 个简单的),很可能你永远不会或不应该。然而,GC 与 Unity 的协程的工作从那时起就被修复了,实际上 MEC 协程现在给 GC 的工作更多了,大约是 10 倍!所以我用协程增加了一个数字,给一个新的协程一个增量,就像开发人员在 2016 年进行的性能测试一样,Unity 的协程支持 Playmode,但在我的案例中产生了 68MB 的垃圾,而 MEC 的则有0,73GB.... 所以我将协程改回了 Unity 协程。