In der Unity3D-Game-Engine lautet eine übliche Codesequenz zum Abrufen von Remote-Daten wie folgt:
WWW www = new WWW("http://remote.com/data/location/with/texture.png");
yield return www;
Was ist der zugrunde liegende Mechanismus hier?
Ich weiß, dass wir den Ertragsmechanismus verwenden, um die Verarbeitung des nächsten Frames zu ermöglichen, während der Download abgeschlossen ist. Aber was ist unter der Haube los, wenn wir das machen?yield return www
?
Welche Methode wird aufgerufen (falls vorhanden in der WWW-Klasse)? Verwendet Unity Threads? Bekommt die "obere" Unity-Ebene eine WWW-Instanz und tut sie etwas?
BEARBEITEN:
- Diese Frage bezieht sich speziell auf Unity3D-Interna. Ich bin nicht an Erklärungen interessiert, wie
yield
Anweisungen in C # funktionieren. Stattdessen suche ich einen Einblick in den Umgang von Unity mit diesen Konstruktionen, um beispielsweise dem WWW das verteilte Herunterladen von Daten über mehrere Frames zu ermöglichen.
yield return
für asynchrone Vorgänge ein Hack ist. In einem "echten" C # -Programm würden SieTask
hierfür a verwenden. Unity verwendet sie wahrscheinlich nicht, da sie vor derTask
Einführung von .Net 4.0 erstellt wurden .Antworten:
Dies ist das C # yield-Schlüsselwort in Aktion - es führt keine besonderen Aktionen mit dem
www
Objekt aus, sondern bedeutet etwas Besonderes für die Methode, in der es enthalten ist. Insbesondere kann dieses Schlüsselwort nur in einer Methode verwendet werden, die einIEnumerable
(oderIEnumerator
) zurückgibt und verwendet wird um anzugeben, welches Objekt vom Enumerator "zurückgegeben" wird, wenn MoveNext aufgerufen wird.Dies funktioniert, weil der Compiler die gesamte Methode in eine separate Klasse konvertiert, die mithilfe einer Zustandsmaschine implementiert
IEnumerable
(oderIEnumerator
). Das Nettoergebnis ist, dass der Hauptteil der Methode selbst erst ausgeführt wird, wenn jemand den Rückgabewert durchführt. Dies funktioniert mit jedem Typ, es ist absolut nichts BesonderesWWW
, sondern die Containing-Methode, die speziell ist.Werfen Sie einen Blick hinter die Kulissen des Schlüsselworts C # yield, um mehr darüber zu erfahren, welche Art von Code der C # -Compiler generiert, oder testen Sie den Code einfach selbst mit etwas wie IL Spy
Update: Zur Verdeutlichung
yield return
Anweisung enthält , wird lediglich ein Enumerator zurückgegeben. Zu diesem Zeitpunkt wird kein Methodentext ausgeführtMoveNext
den Iterator aufrufen , um den ersten Wert in der Sequenz abzurufen. Dies führt dazu, dass die Methode bis zur erstenyeild return
Anweisung ausgeführt wird. An diesem Punkt setzt der Aufrufer die Ausführung fort (und vermutlich wird der Rest des Frames von Unity wiedergegeben).MoveNext
Methode für den Iterator bei jedem folgenden Frame erneut auf, wodurch die Methode bis zur nächstenyield return
Anweisung bei jedem Frame erneut ausgeführt wird, bis entweder das Ende der Methode oder eineyield break
Anweisung erreicht ist (was anzeigt) das Ende der Sequenz)Die einzige spezielle Bit hier (und in einem Paar von anderen Fällen ) ist , dass die Einheit nicht diese besondere Iterator es nur den nächsten Frame, stattdessen geht nicht voran den Iterator (die Methode verursacht weiterhin ausgeführt wird ) , wenn der Download abgeschlossen ist. Obwohl es anscheinend eine Basis- YieldInstruction- Klasse gibt, die vermutlich einen generischen Mechanismus für die Signalisierung an Unity enthält, wenn ein Iterator weiterentwickelt werden soll,
WWW
scheint die Klasse nicht von dieser Klasse zu erben, sodass ich nur davon ausgehen kann, dass es einen Sonderfall für gibt diese Klasse in der Unity-Engine.Nur um klar zu sein: Das
yield
Schlüsselwort hat keine besonderen Auswirkungen auf dieWWW
Klasse, sondern auf die spezielle Behandlung, die Unity den Mitgliedern der zurückgegebenen Aufzählung gibt, die dieses Verhalten verursacht.Aktualisieren Sie die zweite: Für den Mechanismus,
WWW
mit dem Webseiten asynchron heruntergeladen werden, wird wahrscheinlich entweder die HttpWebRequest.BeginGetResponse-Methode verwendet, die intern asynchrone E / A verwendet, oder es können Threads verwendet werden (entweder ein dedizierter Thread erstellt oder ein Threadpool verwendet werden).quelle
WWW
Objekt, wenn es abgegeben wird, sieheWWW
Referenz .yield
scheint hauptsächlich in Unity im Coroutine-Kontext verwendet zu werden. Um mehr über Coroutinen zu erfahren und warum sie C # verwenden,yield
empfehle ich diesen Blog-Artikel: Unity3D-Coroutinen im Detail . Der größte Teil der Forschung in dieser Antwort stammt aus diesem Artikel.Coroutines in Unity werden verwendet, um Aufgaben zu kapseln, die:
Beispiele für solche Aufgaben sind Pfadfindungsberechnungen oder das Abrufen von Daten von einer Website, wie es in Ihrer Frage der Fall ist.
So beantworten Sie Ihre Unterfragen (in einer leicht geänderten Reihenfolge):
Die
WWW
Klasse von Unity ist so konzipiert, dass sie sich aus einer Koroutine ergibt. Gemäß den Kommentaren zu dem Blog-Artikel, der oben verlinkt wurde, enthält der spekulative Codeblock (die "obere" Schicht) überYieldInstruction
s tatsächlich einen Schalter, der auch nach ausgegebenenWWW
s sucht . Dieser Code stellt dann sicher, dass die Coroutine automatisch beendet wird, wenn der Download abgeschlossen ist, wie inWWW
der Referenz beschrieben .In diesem Fall zum Herunterladen der Daten "ohne den Rest des Spiels zu blockieren": Ja, höchstwahrscheinlich. (Und Threading wird definitiv verwendet, um die heruntergeladenen Daten zu dekomprimieren
WWW.threadPriority
.)quelle
Leider ist das WWW intern als systemeigener Code implementiert, sodass wir den Code nicht anzeigen können. Aus Experimenten kann ich das sagen
WWW
wird nicht von abgeleitetYieldInstruction
, was auch immer passiert, wenn Sieyield
es von Sonderfall-Code behandelt werden müssen.Ich habe nie einen Unterschied zwischen gesehen
und
Ich denke, das ist der logischste Weg, es umzusetzen, und höchstwahrscheinlich ist es das, was unter der Haube passiert. Aber ich weiß es nicht genau.
Unity ist nicht einen neuen Thread zum Download starten, zumindest auf einigen Plattformen (iOS, Webplayer). Oder wenn doch, setzt es
WWW.isDone
auf den Hauptthread. Ich weiß das, weil dieser Code:funktioniert nicht
Ich denke nicht, dass Sie spezifischere Antworten haben können, es sei denn, jemand, der Zugriff auf den Quellcode von Unity3d hat, kommt hierher.
quelle
Da Unity3D C # als Scripting-Engine verwendet, nehme ich an, dass es das Standard- Yield-Schlüsselwort ist, das in C # integriert ist. Grundsätzlich bedeutet dies, dass der Wert von www bereits zurückgegeben wird, so dass Sie fortfahren können, während die nächste Iteration den nächsten Wert zurückgibt usw. Yield erstellt im Hintergrund im Grunde eine Zustandsmaschine und einen Iterator.
quelle
WWW
Referenz . Außerdem bin ich mir nicht sicher, ob ichyield
etwas erschaffe. Der Iteratorkontext wird erstellt, indem er implementiertIEnumerable
oder als Rückgabetyp verwendet wird. "State Machine" scheint auch ausgeschaltet zu sein. Sicher, es gibt einen Staat, aber das allein ist kein ausreichendes Eigentum, oder? Vielleicht könnten Sie das näher erläutern.