我有这个方法(在Unity C#Script中),但我不明白“yield”部分是如何工作的。
我从MSDN上知道该函数将返回一个我可以迭代的IEnumerator,但是这段代码等待了1.5秒并且没有迭代,因为这意味着,内部创建的对象被多次创建。 这里的任何人都可以解释一下这段代码的工作原理吗?
IEnumerator DestroyShip()
{
// create new gameobject
Instantiate(ExplosionPrefab, transform.position, transform.rotation);
// make current gameobject invisible
gameObject.renderer.enabled = false;
// set new position for the current gameobject
transform.position = new Vector3(0f, transform.position.y, transform.position.z);
// wait for 1,5 seconds
yield return new WaitForSeconds(1.5f);
// make the current gameobject visible again
gameObject.renderer.enabled = true;
}
答案 0 :(得分:27)
编译器为您生成的枚举器正在迭代。一次。
编译器将生成一个实现IEnumerator的类,该类具有MoveNext()函数和Current属性。该类将具有在调用之间存储函数状态所需的所有成员。确切的细节可以被视为“Compiler Magic”。
此生成的类的对象将由Unity3d Engine处理和管理。 Unity3d引擎将在每个帧上调用每个活动协程上的MoveNext()(除非另有说明)。
这使Unity3d Programmer能够编写一次一帧播放的脚本。 C#编译器魔术和Unity3d Engine魔术的结合使得脚本功能非常强大但易于使用。
回答你的问题:你的函数中的代码将被执行一次,但它会在'yield return'语句中暂停。
如上所述,实现IEnumerator的特殊对象是由C#编译器创建的。
在第一次调用MoveNext()时,您的函数会创建爆炸并将当前对象设置为“new WaitForSeconds(1.5f)”。
Unity3d引擎检查此对象,看到它是特殊类“WaitForSeconds”的实例,因此将枚举器放在某个等待队列上,并且在1.5秒之前不会要求第二个元素。与此同时,将渲染许多帧并播放爆炸。
1.5秒后,Unity将从队列中抓取枚举器,并再次调用MoveNext()。函数的第二部分现在将执行,但无法生成第二个对象。 MoveNext()将返回false以指示它无法获取新元素,这是Unity3d向该枚举器抛出的信号。垃圾收集器将在某个时间点回收内存。
如上所述:许多编译器和Unity3d魔法正在进行中。只要你记得你的函数将被保留到每个yield return语句的下一帧,你就会知道从这些特殊函数中受益。
答案 1 :(得分:5)
如果使用yield
一次,就好像你正在返回一个带有一个元素的IEnumerator,这就是为什么你会得到它不会迭代的印象。
使用yield关键字是一个相当奇怪的用法,很难理解为什么它被实现,所以没有看到整个上下文。
答案 2 :(得分:1)
最好返回IEnumerable
而不是IEnumerator
,因为它更灵活且易于使用。使用IEnumerable<T>
会更好。 yield return只是用于实现迭代器块的语法糖,编译器生成相关代码,因为它本质上是标准的boater板,可以通过编程方式轻松生成。当你想要将一系列单个动作作为一个集合时,你通常会使用yield return,例如:
public IEnumerable<Thing> GetThings()
{
yield return GetNextThing();
}