views:

202

answers:

3

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;
}
+2  A: 

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.

Paolo Tedesco
It gets called in a function called "StartCoroutine": Unity.StartCoroutine(DestroyShip), I think this is very specific to the Unity Engine http://unity3d.com
daemonfire300
Reference: http://unity3d.com/support/documentation/ScriptReference/MonoBehaviour.StartCoroutine.html
daemonfire300
I see... well, this surely makes sense in the context of the Unity engine.
Paolo Tedesco
+1  A: 

It is better to return IEnumerable rather than IEnumerator as it is more flaxible and easy to use. It is even better to use IEnumerable<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.:

public IEnumerable<Thing> GetThings()
{
    yield return GetNextThing();
}
Ben Robinson
Except that the Unity3d engine is the only one using that IEnumerator, and that engine requires it to be an IEnumerable. One can argue that IEnumrable<T> is better, but one also has to play along with the design desissions made by Unity3d.
Sjoerd
+1  A: 

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.

Sjoerd