该函数如何以“产量”发挥作用?工作详细吗?
我得到了这个方法(在 Unity C# 脚本中),但我不明白“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;
}
I got this method (inside a Unity C# Script), but I do not understand how the "yield" part actually works.
I know from the MSDN that the function will return an IEnumerator which I could iterate throught, but this code waits 1,5 seconds and does not get iterated because this would mean, the objects created inside were created multiple times.
Anyone here who can explain me how this code works?
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;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
编译器为您生成的枚举器正在迭代。一次。
编译器将生成一个实现 IEnumerator 的类,该类具有 MoveNext() 函数和 Current 属性。该类将具有在调用之间存储函数状态所需的所有成员。具体细节可以认为是“编译器魔法”。
该生成类的对象将由 Unity3d 引擎处理和管理。 Unity3d 引擎将在每个活动协程上每帧调用一次 MoveNext()(除非另有说明)。
这使得 Unity3d 程序员能够编写一次播放一帧的脚本。 C# 编译器魔法和 Unity3d 引擎魔法的结合产生了非常强大但易于使用的脚本。
回答你的问题:函数中的代码将被执行一次,但它将在“yield return”语句处暂停。
如上所述,C# 编译器创建了一个实现 IEnumerator 的特殊对象。
第一次调用 MoveNext() 时,您的函数会创建爆炸并将当前对象设置为“new WaitForSeconds(1.5f)”。
Unity3d 引擎检查此对象,发现它是特殊类“WaitForSeconds”的实例,因此将枚举器放入某个等待队列中,并且在 1.5 秒过去之前不会请求第二个元素。同时,将渲染许多帧并播放爆炸。
1.5秒后,Unity将从队列中抓取枚举器,并再次调用MoveNext()。函数的第二部分现在将执行,但无法生成第二个对象。 MoveNext() 将返回 false 来指示它无法获取新元素,这是向 Unity3d 发出丢弃此枚举器的信号。垃圾收集器将在某个时间点回收内存。
如前所述:许多编译器和 Unity3d 的魔力正在发生。只要您记住您的函数将被搁置到每个yield return 语句的下一帧,您就会知道足以从这些特殊函数中受益。
The Enumerator the compiler generates for you is being iterated. Once.
The compiler will generate a class that implements IEnumerator, which has a MoveNext() function and a Current property. The class will have all the members required to store the state of the function between calls. The exact details can be considered "Compiler Magic".
The object of this generated class, will be handled and managed by the Unity3d Engine. The Unity3d Engine will call MoveNext() on each active coroutine once every frame (unless instructed otherwise).
This enabled the Unity3d Programmer to write scripts that are played out one frame at a time. A combination of C# compiler magic and Unity3d Engine magic results in very-powerful-but-easy-to-use scripting.
To answer your question: the code in your function will be executed once, but it will pause at the 'yield return' statement.
As stated above, a special object that implements IEnumerator is created by the C# compiler.
On the first call to MoveNext(), your function creates an explosion and sets the current object to "new WaitForSeconds(1.5f)".
The Unity3d engine inspects this object, sees it is an instance of the special class "WaitForSeconds" so puts the enumerator on some waiting queue, and won't ask for the second element until 1.5 sec have passed. In the meantime, many frames will be rendered and the explosion will be played.
After 1.5 sec, Unity will grap the enumerator from the queue, and call MoveNext() again. The second part of your function will execute now, but will fail to generate a second object. MoveNext() will return false to indicate it failed to get a new element, which is the signal to Unity3d to throw this enumerator away. The Garbage Collector will reclaim the memory at some point in time.
As said: lots of compiler and Unity3d magic is going on. As long as you remember that your function will be put on hold till the next frame at each yield return statement, you'll know enough to benefit from those special functions.
如果
yield
使用一次,就好像您返回了一个带有一个元素的 IEnumerator,这就是为什么您会觉得它没有迭代。这是yield 关键字的一个相当奇怪的用法,在不查看整个上下文的情况下很难理解为什么要这样实现。
If
yield
is used once, it is as if you were returning an IEnumerator with one element, that's why you get the impression that it does not iterate.It's a rather strange use of the yield keyword, it's difficult to understand why it was implemented so without seeing the whole context.
最好返回
IEnumerable
而不是IEnumerator
,因为它更灵活且易于使用。使用IEnumerable
效果更好。产量返回只是用于实现迭代器块的语法糖,编译器生成相关代码,因为它本质上是可以通过编程轻松生成的标准样板。当你想让一系列单一操作看起来像是一个集合时,通常使用yield return,例如:It is better to return
IEnumerable
rather thanIEnumerator
as it is more flaxible and easy to use. It is even better to useIEnumerable<T>
. Yield return is just syntactic sugar for implementing an iterator block, the compiler generates the relevent code as it is essentially standard boiler plate that can be easily generated programatically. You generally use yield return when you want to make a series of single actions apear to be a collection, e.g.: