试图了解Unity3D中的c#yield

时间:2016-02-28 20:18:00

标签: c# unity3d

我上课了。它有一个做很多工作的方法。我希望在它完成工作时不要让程序挂起。我理解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我的代码以便程序不会冻结?

5 个答案:

答案 0 :(得分: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运算符,即当Framework第一次调用MoveNext()时 - 代码(A)将被执行,当它第二次调用MoveNext()时 - 代码(B)将被执行,然后是代码(C),依此类推。

答案 1 :(得分:2)

这是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时总是返回主程序。然后它会在下一帧回收。

答案 2 :(得分:1)

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

答案 3 :(得分:0)

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

尝试做:

var result = DoWork().ToList();

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

答案 4 :(得分:0)

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使用此语言功能来实现Coroutines:

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

现在,Unity会定期检查Current属性,并根据其值决定是否再次呼叫MoveNext()Current的值可能是WaitForEndOfFrame的实例,WWW的实例或其他任何内容,并且根据时间,MoveNext()被调用。

再次调用MoveNext()后,MyEnumerationMethod()的执行将在上次中断时继续执行,并执行直到执行下一个yield return。等等。

这就是屈服,而团结中的Coroutines。