尝试了解 Unity3D 中的 c# 产量

本文关键字:产量 中的 Unity3D 了解 | 更新日期: 2023-09-27 18:36:47

我有一个类。它有一个可以做很多工作的方法。我不希望在程序完成工作时挂起。我知道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我的代码,以便程序不会冻结?

尝试了解 Unity3D 中的 c# 产量

这是 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;
  }
}

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

 yield return new WaitForEndOfFrame();

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

这是一种执行一些重复操作的方法,但在达到产量时始终返回到主程序。然后它将在下一帧从那里回来。

您需要使用 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),依此类推。

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

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

尝试执行以下操作:

var result = DoWork().ToList();

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

Unity 中的 C#

yield就像 C# yields 一样工作。团结不会以任何方式影响这一点。

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()返回,可以通过查看枚举器的 Current 属性来检索第一个结果 (5)。

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

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

这就是 Unity 中屈服和协程的全部内容。