unity3d 什么是C#中的IEnumerator,它在Unity中有什么用途?

m0rkklqb  于 2023-03-23  发布在  C#
关注(0)|答案(3)|浏览(137)

最近我看到了一个在Unity中使用C#生成对象的教程。

public IEnumerator CallSpawner()
   {
      yield return new WaitForSeconds(0.5f);
      SpawnObstacles();
   }

我想问一下IEnumerator函数有什么用,我们不能通过这个过程来实现吗

float diffTime = 0f;
private void Update()
{
    if(Time.time - diffTime == 0.5f)
    {
        diffTime = Time.time;
        SpawnObstacles();
    }
}

我读了文档,但无法理解它。

e4eetjau

e4eetjau1#

IEnumerator函数的用途是什么
IEnumerator没有函数,它是一个 * 返回类型 *。C#也没有函数(但我知道你的意思)-在C#中我们称它们为 * 方法 *。

IEnumerator是一个接口,所以任何实现了IEnumerator接口的类都可以通过这个方法返回

在实践中,在这种使用中,它看起来实际上更像是一种黑客,而不是打算提供枚举器的真正意图,即一步一步地步枪通过(或生成)一个事物的集合。
当你在一个方法中使用一个yield return语句时,“一些神奇的事情发生了”,它不是一个经典意义上的return,而是创建了一个工具,代码可以从它停止的地方恢复(从返回的枚举器中调用下一个项将导致代码从yield之后恢复,使用它之前的所有状态,而不是重新开始)。
如果你看一下MSDN的yield示例:

public class PowersOf2
{
    static void Main()
    {
        // Display powers of 2 up to the exponent of 8:
        foreach (int i in Power(2, 8))
        {
            Console.Write("{0} ", i);
        }
    }

    public static System.Collections.Generic.IEnumerable<int> Power(int number, int exponent)
    {
        int result = 1;

        for (int i = 0; i < exponent; i++)
        {
            result = result * number;
            yield return result;
        }
    }

    // Output: 2 4 8 16 32 64 128 256
}

环路由i控制;如果这不是一个yield return,那么这将不会像预期的那样运行(它不能返回一个枚举数作为开始,但我们将忽略它)。代码将进入,开始循环,命中return,并只返回一个数字一次,所有的内存中的循环将被遗忘。
通过将其设置为yield return,将返回一个枚举数,并设置一个小的“保存状态”集,从而使循环可以记住i的当前值每次请求下一个值时,代码将从中断处继续执行。(即刚屈服后)循环再次循环,并产生不同的值。2这继续到最大值。此时返回的枚举数表示它没有更多的项
你也可以永远地产生……如果代码永远不能逃脱循环,那么它将永远地产生/生成
在这种情况下,你必须使用yield return new WaitForSeconds,因为这就是WaitForSeconds的工作方式。Yielding将枚举器交给调用方法,然后调用方法可以自由地枚举它。从文档来看,这似乎是故意在下一帧上完成的,所以利用收益率(可能重复)是一种安排代码块的方法,该代码块跨多个帧出现,而无需某种外部状态管理来记住过程是一个冗长的

  • 如果状态= 1则关闭门并将状态加1,
  • 否则,如果状态= 2,则点亮手电筒并加1
  • else if state = 3 ..."。

你可以

  • 屈服,
  • 关上门
  • 屈服,
  • 点燃 Torch
  • yield..

难道我们不能通过这个过程
当然,看起来很合理;每秒看100次时钟,如果从你第一次看时钟开始已经过了0.5秒,产生障碍
我会想象(从未使用过Unity;不要自称知道任何关于它的东西,除了读过这个函数的文档),你的更新循环有更多的事情要做,所以把一个进程交给一个专门的wait-then-do比花所有的时间看时钟和执行一个可能复杂的calc来计算你是否应该做某事更有效;生活中的大多数事情,一开始都是每隔x毫秒轮询一次,现在都可以从“如果事件发生,就做出React”的工作方式中受益

aydmsdu9

aydmsdu92#

您正在查看的代码仅存在于Unity3d和DotNet Framework〈4.0中。
你找不到任何相关文档的原因是:
1.这是一个邪恶的黑客(由Jeffrey Richter首创),它滥用了C#编译器的IEnumerable状态机生成器特性。
1.微软的家伙们早就把这个黑客添加到dot net编译器中了,他们把这个功能称为async/await
不幸的是,Unity3d基于旧版本的Mono,它不支持async/await
幸运的是,async/await的大多数特性都可以使用这种方法来实现
有几个理由支持Jeff的强力线程库方法。
1.您有一个方法来设置等待、定义等待时间和执行有效负载,并按此顺序执行。
1.性能。您建议的代码每秒将运行多次,当您轮询超时完成时会减慢系统速度。相反,PTL方法提供了一个回调事件,该事件将在将来调度。
1.清理。您的代码将继续运行,在SpawnObstacles()运行一次后检查0.5长。
1.模糊计时。如果你的电脑由于延迟,性能等原因跳帧,你可能已经完全跳过了0.5帧。更重要的是,有可能,0.5d永远不会发生(请参阅Jon Skeet的Pony Fail帖子,你正在做一个双对双比较)。计时器/回调肯定会在0.5秒后的某个时间触发。
但主要的原因必须是心流。在这个非常简单的例子中,我们不会轻易混淆。然而,想象一下以下效果。

  • 每10秒就会有新的敌人出现
  • 每次产卵都将遵循fibonacci序列
  • 在10 s时会有1个敌人出现
  • 在20 s 1敌人
  • 在30 s 2敌人
  • 在40 s 3敌人
  • 在50 s 5敌人
  • 在60 s 8敌人
  • 等等

update方法来写这段代码是相当困难的。
但是,您可以轻松地使用IEnumerable。

public IEnumerator CallSpawner()
{
  int current = 1;
  int last = 0;
  while(true)
  {
      yield return new WaitForSeconds(10f);
      SpawnEnemies(current);
      var next = current + last;
      last = current;
      current = next;
   }
}

作为奖励,您将注意到所有变量都很好地封装在Method中,而不是自由浮动的字段中。
这意味着您可以运行多个CallSpawner()而不会受到它们的干扰。

dauxcl2d

dauxcl2d3#

协程是一种编写代码的方法,它说“在队列中等待一会儿”。
更准确地说,WaitForSeconds和“wait until the next frame”(yield return null)是让代码在一个位置等待的命令。它们只能在协程内部使用。

相关问题