Ich verstehe das Prinzip der Coroutinen. Ich weiß, wie man den Standard StartCoroutine
/ das yield return
Muster in C # in Unity zum Laufen bringt, z. B. eine Methode aufruft, die IEnumerator
über zurückkehrt, StartCoroutine
und in dieser Methode etwas tut yield return new WaitForSeconds(1);
, eine Sekunde wartet und dann etwas anderes tut.
Meine Frage ist: Was ist wirklich hinter den Kulissen los? Was macht das StartCoroutine
wirklich? Was IEnumerator
kommt WaitForSeconds
zurück? Wie StartCoroutine
kehrt die Steuerung zum "etwas anderen" Teil der aufgerufenen Methode zurück? Wie interagiert all dies mit dem Parallelitätsmodell von Unity (bei dem viele Dinge gleichzeitig ohne Verwendung von Coroutinen ablaufen)?
IEnumerator
/IEnumerable
(oder die generischen Entsprechungen) zurückgeben und dasyield
Schlüsselwort enthalten . Nach Iteratoren suchen.Antworten:
Der häufig referenzierte Link zu Unity3D-Coroutinen im Detail ist tot. Da es in den Kommentaren und Antworten erwähnt wird, werde ich den Inhalt des Artikels hier veröffentlichen. Dieser Inhalt stammt aus diesem Spiegel .
quelle
Die erste Überschrift unten ist eine klare Antwort auf die Frage. Die beiden folgenden Überschriften sind für den täglichen Programmierer nützlicher.
Möglicherweise langweilige Implementierungsdetails von Coroutinen
Coroutinen werden in Wikipedia und anderswo erklärt. Hier werde ich nur einige Details aus praktischer Sicht liefern.
IEnumerator
,yield
usw. sind C # -Sprachfunktionen , die in Unity für einen etwas anderen Zweck verwendet werden.Um es ganz einfach auszudrücken: Ein
IEnumerator
Anspruch auf eine Sammlung von Werten, die Sie einzeln anfordern können, ähnlich wie aList
. In C # muss eine Funktion mit einer Signatur zum Zurückgeben einerIEnumerator
nicht tatsächlich eine Signatur erstellen und zurückgeben, sondern kann C # implizit bereitstellen lassenIEnumerator
. Die Funktion kann dann den Inhalt dessen, wasIEnumerator
in Zukunft zurückgegeben wird, durchyield return
Anweisungen auf träge Weise bereitstellen . Jedes Mal, wenn der Aufrufer einen anderen Wert von diesem impliziten Wert anfordertIEnumerator
, wird die Funktion bis zur nächstenyield return
Anweisung ausgeführt, die den nächsten Wert bereitstellt. Als Nebenprodukt wird die Funktion angehalten, bis der nächste Wert angefordert wird.In Unity verwenden wir diese nicht, um zukünftige Werte bereitzustellen. Wir nutzen die Tatsache aus, dass die Funktion angehalten wird. Aufgrund dieser Ausbeutung sind viele Dinge über Coroutinen in Unity nicht sinnvoll (Was hat
IEnumerator
mit irgendetwas zu tun? Was istyield
? Warumnew WaitForSeconds(3)
? Usw.). Was "unter der Haube" passiert, ist, dass die Werte, die Sie über den IEnumerator bereitstellen, verwendet werden, umStartCoroutine()
zu entscheiden, wann nach dem nächsten Wert gefragt werden soll, der bestimmt, wann Ihre Coroutine wieder unterbrochen wird.Dein Unity-Spiel ist Single Threaded (*)
Coroutinen sind keine Fäden. Es gibt eine Hauptschleife von Unity, und alle Funktionen, die Sie schreiben, werden nacheinander von demselben Hauptthread aufgerufen. Sie können dies überprüfen, indem Sie ein
while(true);
in eine Ihrer Funktionen oder Coroutinen einfügen. Es wird das Ganze einfrieren, sogar den Unity-Editor. Dies ist ein Beweis dafür, dass alles in einem Hauptthread läuft. Dieser Link , den Kay in seinem obigen Kommentar erwähnt hat, ist auch eine großartige Ressource.(*) Unity ruft Ihre Funktionen von einem Thread aus auf. Sofern Sie keinen Thread selbst erstellen, ist der von Ihnen geschriebene Code ein einzelner Thread. Natürlich verwendet Unity andere Threads und Sie können Threads selbst erstellen, wenn Sie möchten.
Eine praktische Beschreibung der Coroutinen für Spielprogrammierer
Grundsätzlich, wenn Sie anrufen
StartCoroutine(MyCoroutine())
, es ist genau wie bei einem normalen FunktionsaufrufMyCoroutine()
, bis die erstenyield return X
, in demX
so etwas wie istnull
,new WaitForSeconds(3)
,StartCoroutine(AnotherCoroutine())
,break
etc. Dies ist , wenn es beginnt , von einer Funktion unterscheidet. Unity "pausiert" diese Funktion direkt in dieseryield return X
Zeile, fährt mit anderen Geschäften fort und einige Frames vergehen, und wenn es wieder soweit ist, nimmt Unity diese Funktion direkt nach dieser Zeile wieder auf. Es speichert die Werte für alle lokalen Variablen in der Funktion. Auf diese Weise können Siefor
beispielsweise alle zwei Sekunden eine Schleife erstellen.Wann Unity wieder aufgenommen wird, hängt davon ab, was
X
sich in Ihrer Coroutine befandyield return X
. Wenn Sie beispielsweise verwendet habenyield return new WaitForSeconds(3);
, wird es nach Ablauf von 3 Sekunden fortgesetzt. Wenn Sie verwendet habenyield return StartCoroutine(AnotherCoroutine())
, wird es fortgesetzt, nachdemAnotherCoroutine()
es vollständig abgeschlossen ist, sodass Sie das Verhalten rechtzeitig verschachteln können. Wenn Sie gerade a verwendet habenyield return null;
, wird es direkt beim nächsten Frame fortgesetzt.quelle
Einfacher geht es nicht:
Unity (und alle Game Engines) basieren auf Frames .
Der ganze Punkt, die ganze Existenzberechtigung der Einheit, ist, dass sie rahmenbasiert ist. Der Motor erledigt "jeden Frame" für Sie. (Animiert, rendert Objekte, macht Physik und so weiter.)
Sie könnten fragen ... "Oh, das ist großartig. Was ist, wenn ich möchte, dass der Motor in jedem Frame etwas für mich tut? Wie kann ich dem Motor sagen, dass er das und das in einem Frame tun soll?"
Die Antwort ist ...
Genau dafür ist eine "Coroutine" gedacht.
So einfach ist das.
Und bedenken Sie dies ....
Sie kennen die Funktion "Update". Ganz einfach, alles, was Sie dort eingeben, wird in jedem Frame ausgeführt . Es ist buchstäblich genau das gleiche, überhaupt kein Unterschied zur Coroutine-Yield-Syntax.
Es gibt absolut keinen Unterschied.
Fußnote: Wie jeder betont hat, hat Unity einfach keine Fäden . Die "Frames" in Unity oder einer anderen Game-Engine haben keinerlei Verbindung zu Threads.
Coroutinen / Ausbeute sind einfach, wie Sie auf die Frames in Unity zugreifen. Das ist es. (Und in der Tat ist es absolut dasselbe wie die von Unity bereitgestellte Update () - Funktion.) Das ist alles, es ist so einfach.
quelle
Update()
? Ich meine, es sollte zumindest einen kleinen Unterschied zwischen diesen beiden Implementierungen und ihren Anwendungsfällen geben, was ziemlich offensichtlich ist.Habe mich in letzter Zeit damit befasst und hier einen Beitrag geschrieben - http://eppz.eu/blog/understanding-ienumerator-in-unity-3d/ - der die Interna (mit dichten Codebeispielen), die zugrunde liegende
IEnumerator
Schnittstelle, beleuchtet. und wie es für Coroutinen verwendet wird.quelle
Die Basisfunktionen in Unity, die Sie automatisch erhalten, sind die Funktionen Start () und Update (). Coroutines sind also im Wesentlichen Funktionen wie die Funktionen Start () und Update (). Jede alte Funktion func () kann genauso aufgerufen werden wie eine Coroutine. Die Einheit hat offensichtlich bestimmte Grenzen für Coroutinen gesetzt, die sie von regulären Funktionen unterscheiden. Ein Unterschied ist statt
Du schreibst
für Coroutinen. Und genauso können Sie die Zeit in normalen Funktionen mit Codezeilen wie steuern
Eine Coroutine hat einen bestimmten Griff, wie die Zeit gesteuert werden kann.
Obwohl dies nicht das einzige ist, was in einem IEnumerator / Coroutine möglich ist, ist es eines der nützlichen Dinge, für die Coroutines verwendet werden. Sie müssten die Skript-API von Unity untersuchen, um andere spezifische Verwendungen von Coroutines zu erfahren.
quelle
StartCoroutine ist eine Methode zum Aufrufen einer IEnumerator-Funktion. Es ähnelt dem Aufrufen einer einfachen Void-Funktion. Der Unterschied besteht jedoch darin, dass Sie sie für IEnumerator-Funktionen verwenden. Diese Art von Funktion ist einzigartig, da Sie damit eine spezielle Ertragsfunktion verwenden können. Beachten Sie, dass Sie etwas zurückgeben müssen. Soweit ich weiß. Hier habe ich ein einfaches Flackerspiel über die Textmethode in Einheit geschrieben
Ich habe es dann aus dem IEnumerator heraus aufgerufen
Wie Sie sehen können, habe ich die StartCoroutine () -Methode verwendet. Hoffe ich habe irgendwie geholfen. Ich bin selbst ein Anfänger. Wenn Sie mich also korrigieren oder schätzen, wäre jede Art von Feedback großartig.
quelle