【问题标题】:Trying to understand c# yield in Unity3D试图理解 Unity3D 中的 c# yield
【发布时间】:2016-06-11 19:05:33
【问题描述】:

我有课。它有一种可以做很多工作的方法。我不想让程序在工作时挂起。我知道yield 会为我做这件事。

void Start() {
  DoWork(10,10);
}

void DoWork (int x, int y) {
  for (int i=0; i < x; i++) {
    for (int j=0; j < y; j++) {
      // Stuff
    }
  }
}

如果我像这样添加yield

void Start() {
  DoWork(10, 10);
}

IEnumerable DoWork (int x, int y) {
  for (int i=0; i < x; i++) {
    for (int j=0; j < y; j++) {
      // Stuff
    }
    Debug.Log (1);
    yield return null;
  }
}

没有完成任何工作,除此之外,我根本看不到任何日志语句。

我如何yield 我的代码使程序不会死机?

【问题讨论】:

    标签: c# unity3d


    【解决方案1】:

    这是 Unity3D 引擎,因此您的协程需要返回 IEnumerator 才能有效:

    void Start() {
      StartCoroutine(DoWork(10, 10));
    }
    
    IEnumerator DoWork (int x, int y) {
      for (int i=0; i < x; i++) {
        for (int j=0; j < y; j++) {
          // Stuff
        }
        Debug.Log (1);
        yield return null;
      }
    }
    

    这绝不是多线程。它就像在 Update 和 LateUpdate 之间每帧更新一次一样运行,除非您使用

     yield return new WaitForEndOfFrame();
    

    然后它被推迟到渲染过程之后。它所做的是创建一个 Coroutine 类型的新对象,并将其放置在调用 MonoBehaviour 协程堆栈上。

    这是一种执行某些重复操作但总是在遇到 yield 时返回主程序的方法。然后它将在下一帧从那里赶回来。

    【讨论】:

    • 嗨@贾斯汀。我担心你可能仍然对 Unity3D 中的MonoBehavior(协程)有误解。这是一篇愤怒的文章stackoverflow.com/a/35280256/294884
    • 还有 Justin,我非常担心,既然您谈到了“工作”,那么您实际上是在考虑线程,而不是基于框架游戏引擎中的概念。 Unity 中的帧循环(即“更新”,或者如果您愿意的话,实际上是“协程”——这完全是一回事)什么都不是,它只是时间中的帧步骤。一般来说,你不能真正“拆分”那里的工作,我相信你的思维方式。 (当然,你可以在任何给定的框架中“一些”一些东西。)
    • 这是一篇美丽、优雅的.. 确实,你可以说诙谐有见地.. 可爱,真的.. 来自醉酒的answers.unity3d.com/answers/1147932/view.html
    • @JoeBlow 哈哈。每天早上喝咖啡时阅读您的 Unity cmets 总是一件有趣的事:)
    • 嘿,太棒了。不幸的是,我只是在喝一些晚上的咖啡馆:/
    【解决方案2】:

    你需要使用StartCoroutine方法:

    void Start() {
      StartCoroutine(DoWork(10, 10));
    }
    
    IEnumerator DoWork (int x, int y) {
      // (A)
      yield return null;
      // (B)
      for (int i=0; i < x; i++) {
        for (int j=0; j < y; j++) {
          // Stuff
        }
        Debug.Log (1);
        yield return null;
        // (C)
      }
    }
    

    你的代码是逐段执行的,其中步骤的分隔符是 yield 运算符,即当框架第一次调用 MoveNext() 时 - 代码 (A) 将被执行,当它第二次调用 MoveNext() 时 - 代码(B) 将被执行,然后是代码 (C),依此类推。

    【讨论】:

    • 我认为你被Unity标签误导了,这对Unity3d引擎无效。那甚至不会编译。协程必须返回 IEnumerator 而不是 IEnumerable。第二部分根本不是 Unity3D 对协程所做的,所以我问你在回答一些不同的问题。
    • 这似乎与 DoWork 方法应该是 IEnumerator 不同。事情和以前一样长,但程序在完成之前不再挂起。
    • 您应该删除答案的第二部分,因为您假设了一些您实际上不知道的信息。顺便说一句,它们很可能是错误的,Unity 协程不是多线程的。
    • 我在没有任何使用 unity3d 经验的情况下接近真相 :)
    【解决方案3】:

    当您添加yield 语句时,编译器实际上会生成一个私有类,该类充当实现IEnumerable 的状态机。因此,除非您枚举方法的结果,否则不会调用从原始方法包装的任何代码 - 在您的示例中,您丢弃了返回值,因此不会发生任何事情。

    【讨论】:

    • rowland,这是专门针对 Unity3D 中的MonoBehavior
    • @JoeBlow 无论这是控制台应用程序还是 Unity 应用程序,答案都是正确的 - 问题不在于 yield 做了什么,或者您必须枚举其结果才能真正实现做点什么。
    【解决方案4】:

    Yield 关键字用于 C# 中的延迟加载/计算支持。

    尝试做:

    var result = DoWork().ToList();
    

    这会强制对 DoWork() 方法进行评估,您将看到正在发生日志记录。

    【讨论】:

    • tamas,这是专门针对 Unity3D 中的 MonoBehavior
    【解决方案5】:

    Unity 中的 C# yield 就像 C# yield 一样。 Unity 不会以任何方式影响这一点。

    yield 是一个关键字,用于允许对一组返回值进行枚举。

    IEnumerator<int> MyEnumerationMethod()
    {
        yield return 5;
        yield return 1;
        yield return 9;
        yield return 4;
    }
    
    void UserMethod1()
    {
        foreach (int retVal in MyEnumerationMethod())
            Console.Write(retVal + ", ");
    
        // this does print out 5, 1, 9, 4, 
    }
    
    void UserMethod2()
    {
        IEnumerator<int> myEnumerator = MyEnumerationMethod();
        while (myEnumerator.MoveNext())
            Console.Write(myEnumerator.Current + ", ");
    
        // this does print out 5, 1, 9, 4, 
    }
    

    UserMethod1() 和 UserMethod2() 几乎相同。 UserMethod1() 只是 UserMethod2() 的 C# 语法糖版本。

    Unity 使用这种语言特性来实现协程:

    当您调用StartCoroutine() 并将IEnumerator 传递给它时,Unity 会存储此枚举器并首次调用MoveNext()。这将导致MyEnumerationMethod() 被调用并执行到第一个yield return。此时,MoveNext() 返回,第一个结果 (5) 可以通过查看枚举器的 Current 属性来检索。

    现在,Unity 会定期检查Current 属性,并根据其值决定是否该再次调用MoveNext()Current 的值可能是WaitForEndOfFrame 的实例、WWW 的实例或其他任何东西,并根据时间决定调用MoveNext()

    一旦再次调用MoveNext()MyEnumerationMethod() 的执行将在上次中断的地方继续执行,直到执行下一个yield return。以此类推。

    这就是 Unity 中的 yield 和 Coroutines 的全部内容。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-09-01
      • 2013-04-06
      • 1970-01-01
      • 2013-07-05
      • 2016-10-15
      • 2013-10-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多