2010-08-09 20 views
10

Ich habe diese Methode (in einem Unity C# Skript), aber ich verstehe nicht, wie der "Ausbeute" Teil tatsächlich funktioniert.Wie funktioniert diese Funktion mit einem "Ertrag" im Detail?

Ich weiß von der MSDN, dass die Funktion einen IEnumerator zurückgibt, den ich durchlaufen konnte, aber dieser Code wartet 1,5 Sekunden und wird nicht iteriert, da dies bedeuten würde, dass die erstellten Objekte mehrfach erstellt wurden. Jeder hier, der mir erklären kann, wie dieser Code funktioniert?

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

Der Titel der Frage könnte anschaulicher sein. Irgendwie hat Google die Keywords trotzdem abgeholt. – ftvs

Antwort

21

Der Enumerator der Compiler generiert für Sie iteriert wird. Einmal.

Der Compiler generiert eine Klasse, die IEnumerator implementiert, die über eine MoveNext() - Funktion und eine Current-Eigenschaft verfügt. Die Klasse enthält alle Elemente, die zum Speichern des Funktionszustands zwischen den Aufrufen erforderlich sind. Die genauen Details können als "Compiler Magic" betrachtet werden.

Das Objekt dieser generierten Klasse wird von der Unity3d Engine verwaltet und verwaltet. Die Unity3d-Engine ruft MoveNext() für jede aktive Coroutine einmal pro Frame auf (sofern nicht anders angewiesen).

Dadurch konnte der Unity3d Programmer Skripte schreiben, die Frame für Frame abgespielt werden. Eine Kombination aus C# -Compiler-Magie und Unity3D-Engine-Magie führt zu sehr leistungsstarkem, aber einfach zu verwendendem Scripting.

Um Ihre Frage zu beantworten: der Code in Ihrer Funktion wird einmal ausgeführt, aber es wird bei der "Yield Return" -Anweisung pausieren.

Wie oben erwähnt, wird ein spezielles Objekt, das IEnumerator implementiert, vom C# -Compiler erstellt.

Beim ersten Aufruf von MoveNext() erstellt Ihre Funktion eine Explosion und setzt das aktuelle Objekt auf "new WaitForSeconds (1.5f)".

Die Unity3d-Engine prüft dieses Objekt, erkennt, dass es eine Instanz der speziellen Klasse "WaitForSeconds" ist, setzt den Enumerator in eine Warteschlange und fragt erst nach 1,5 Sekunden nach dem zweiten Element. In der Zwischenzeit werden viele Frames gerendert und die Explosion wird abgespielt.

Nach 1,5 Sek. Wird Unity den Enumerator aus der Warteschlange übernehmen und MoveNext() erneut aufrufen. Der zweite Teil Ihrer Funktion wird jetzt ausgeführt, es wird jedoch kein zweites Objekt generiert. MoveNext() gibt false zurück, um anzuzeigen, dass es kein neues Element erhalten hat. Dies ist das Signal an Unity3d, diesen Enumerator wegzuwerfen. Der Garbage Collector wird den Speicher zu einem bestimmten Zeitpunkt zurückfordern.

Wie gesagt: Viele Compiler und Unity3d Magie geht weiter. Solange Sie sich daran erinnern, dass Ihre Funktion bis zum nächsten Frame bei jeder Rendite-Return-Anweisung gehalten wird, wissen Sie genug, um von diesen speziellen Funktionen zu profitieren.

+0

Das Schreiben dieses Codes in ein eigenständiges C# -Projekt und das Betrachten der generierten .net-Assembly in ILSPy ist ein schneller Blickfang dafür, wie all die Magie funktioniert. –

5

Wenn yield einmal verwendet wird, ist es, als ob Sie ein IEnumerator mit einem Element zurückkehrten, deshalb hat man den Eindruck, dass es nicht iterieren ist.

Es ist eine ziemlich seltsame Verwendung des Yield-Schlüsselwortes, es ist schwer zu verstehen, warum es implementiert wurde, ohne den gesamten Kontext zu sehen.

+1

Es wird in einer Funktion namens "StartCoroutine" aufgerufen: Unity.StartCoroutine (DestroyShip), ich denke, das ist sehr spezifisch für die Unity Engine http://unity3d.com –

+0

Referenz: http://unity3d.com/support/documentation /ScriptReference/MonoBehaviour.StartCoroutine.html –

+1

Ich sehe ... nun, das macht im Zusammenhang mit der Unity-Engine sicherlich Sinn. –

1

Es ist besser, IEnumerable anstatt IEnumerator zurückzugeben, da es fl exibles und einfach zu verwenden ist. Es ist sogar besser, IEnumerable<T> zu verwenden. Ertragsrendite ist nur syntaktischer Zucker für die Implementierung eines Iteratorblocks, der Compiler erzeugt den relevanten Code, da es sich im Wesentlichen um eine Standardkesselplatte handelt, die einfach programmatisch erzeugt werden kann. Sie verwenden Ertrag Rückkehr im Allgemeinen, wenn Sie eine Reihe von einzelnen Aktionen machen wollen apear eine Sammlung zu sein, z.B .:

public IEnumerable<Thing> GetThings() 
{ 
    yield return GetNextThing(); 
} 
+1

Außer, dass die Unity3d-Engine die einzige ist, die IEnumerator verwendet, und diese Engine erfordert, dass es ein IEnumerable ist. Man kann argumentieren, dass IEnumrable besser ist, aber man muss auch mit den Design Units von Unity3d spielen. – Sjoerd