在 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

简单用法

unity forum中提到的一般Coroutines对于在多个帧上执行方法很有用[然后忘记它]。”

如果您打算只使用其中的几个(少于 10k?),那么您也可以使用 Unity Coroutines

高级用法

目前 Unity 支持 Task/Async,但性能仍然很低。因此,您可能会考虑使用 Coroutines 来模拟 Async 功能。

在这种情况下,您甚至可以使用 Coroutines 删除所有(或大部分)Update 循环,例如您发布的示例。

这在性能方面很有用,尤其是在您的特定情况下,因为当您使用 Update 循环时,即使您什么都不做,您也会不断地检查 if (timer < 0)。甚至一个空的更新循环仍然是一个性能问题。

另一方面,您可能只是在事件发生时开始和停止 Coroutines。当协程停止时,性能成本变为 0。

如果您想遵循此方法,我强烈建议使用 MEC coroutines,它可以消除由 Unity Coroutines 引起的所有性能问题(并具有相同的功能)。


结论

  1. 在大多数情况下,这种性能差异无关紧要
  2. MEC Coroutines 比更新循环
  3. 性能更高
  4. Update 循环通常比 Unity Coroutines
  5. 更高效
  6. 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 协程。