Ich bin neu in Unity. Ich habe Coroutinen gelernt und das geschrieben.
private void Fire()
{
if(Input.GetButtonDown("Fire1"))
{
StartCoroutine(FireContinuously());
}
if(Input.GetButtonUp("Fire1"))
{
StopAllCoroutines();
}
}
IEnumerator FireContinuously()
{
while(true)
{
GameObject laser = Instantiate(LaserPrefab, transform.position, Quaternion.identity) as GameObject;
laser.GetComponent<Rigidbody2D>().velocity = new Vector2(0, 10f);
yield return new WaitForSeconds(firetime);
}
}
Wenn die Taste gedrückt wird, wird die Coroutine aufgerufen und sie tritt in die 'while'-Schleife ein. Wenn ich den Knopf verlasse, stoppt es die Coroutine. Sollte es nicht in der 'while'-Schleife stecken bleiben, da es sich um eine Endlosschleife handelt? Warum?
unity
c#
coroutines
Babyhirn
quelle
quelle
"Fire1"
. Können Sie dies in der Engine einrichten, um Neuzuordnungen von Schlüsseln zu ermöglichen, anstatt sie auszutippenKeycode.Foo
?yield
die Abkürzung für "Ertragskontrolle für den Anrufer, bis das nächste Element in der Aufzählung angefordert wird" effektiv ist.StopAllCoroutines()
in diesem Fall nicht. Es ist in Ordnung, wenn Sie immer nur eine Coroutine verwenden, aber wenn Sie jemals mehr als eine Coroutine geplant haben, hätte dies unerwünschte Auswirkungen. Stattdessen sollten SieStopCoroutine()
anstelle aller alle die relevante verwenden und einfach stoppen. (StopAllCoroutines()
wäre zB nützlich, wenn du das Level beendest oder einen neuen Bereich lädst usw., aber nicht für bestimmte Dinge wie "Ich schieße nicht mehr".)Antworten:
Der Grund ist das Schlüsselwort,
yield
das in C # eine bestimmte Bedeutung hat.Bei der Begegnung mit den Wörtern
yield return
kehrt erwartungsgemäß eine Funktion in C # zurück.Es gibt also keine Endlosschleife. Es gibt eine Funktion / einen Iterator, die unendlich oft aufgerufen werden kann.
Mit der Unity-Funktion
StartCoroutine()
ruft das Unity-Framework die Funktion / den Iterator einmal pro Frame auf.Die Unity-Funktion
StopAllCoroutines
bewirkt, dass das Unity-Framework den Aufruf der Funktion / des Iterators beendet.Und der Rückkehr
WaitForSeconds(time)
aus dem Iterator macht die Unity Rahmen suspend Aufruf der Funktion / Iterator fürtime
.Ein verwirrter Kommentar und eine ebenso verwirrte Gegenstimme zu diesem Kommentar ermutigten mich, näher darauf einzugehen, was das Keyword
yield
tut und was nicht.Wenn Sie dies schreiben:
Sie können stattdessen auch Folgendes schreiben:
Daraus folgt, dass das Schlüsselwort
yield
nicht mit Multithreading zusammenhängt und absolut nicht aufruftSystem.Threading.Thread.Yield()
.quelle
On encountering the words yield return a function in C# returns
". Nein, tut es nicht. Der Text, den Sie zitieren, erklärt es ebenso wie Wikipedia - "In computer science, yield is an action that occurs in a computer program during multithreading, of forcing a processor to relinquish control of the current running thread, and sending it to the end of the running queue, of the same scheduling priority.
". Grundsätzlich: "Bitte halten Sie mich an, wo ich bin, und lassen Sie jemanden eine Weile laufen."Wenn der Feuerknopf gedrückt wird, wird die zweite if-Anweisung eingegeben und StopAllCoroutines ausgeführt. Dies bedeutet, dass die Coroutine, in der die while-Schleife ausgeführt wird, beendet wird, sodass keine Endlosschleife mehr vorhanden ist. Die Coroutine ist wie ein Container, in dem der Code ausgeführt werden soll.
Ich kann das Unity-Handbuch und die Unity Scripting-API empfehlen , um besser zu verstehen, was Coroutinen sind und wie leistungsfähig sie sein können.
Dieser Blog- und Youtube-Beitrag war auch hilfreich für mich, um Coroutinen besser zu nutzen.
quelle
Coroutinen sind ein seltsames Tier. Yield Return bewirkt, dass die Methode die Ausführung anhält, bis sie später schrittweise ausgeführt wird. Hinter den Kulissen könnte es ungefähr so aussehen:
Und innerhalb von Unity / C # (da Yield Return eine native C # -Funktion ist) wird beim Aufrufen von StartCoroutine ein
FireContinuouslyData
Objekt erstellt und an die Methode übergeben. Basierend auf dem Rückgabewert wird festgelegt, wann es später erneut aufgerufen werden soll, indem einfach das FireContinuouslyData-Objekt gespeichert wird, um es beim nächsten Mal zu übergeben.Wenn Sie jemals eine Ertragsunterbrechung durchgeführt haben, könnte diese intern nur festgelegt werden,
data.shouldBreak = true
und dann würde Unity die Daten einfach wegwerfen und nicht erneut planen.Und wenn zwischen den Ausführungen Daten gespeichert werden müssten, würden diese auch für später in den Daten gespeichert.
Ein Beispiel dafür, wie Unity / C # die Coroutine-Funktionalität implementieren könnte:
quelle
In einer anderen Antwort wird erwähnt, dass Sie die Co-Routinen stoppen, wenn
"Fire1"
sie aktiv sind. Dies ist insofern völlig korrekt, als die Coroutine GameObjects nach dem ersten Drücken von nicht weiter instanziiert"Fire1"
.In Ihrem Fall bleibt dieser Code jedoch nicht in einer Endlosschleife stecken. So wie es aussieht, suchen Sie nach einer Antwort auf die
while(true) {}
Schleife, auch wenn Sie sie nicht extern gestoppt haben.Es wird nicht stecken bleiben, aber Ihre Coroutine wird auch nicht enden (ohne anzurufen
StopCoroutine()
oderStopAllCoroutines()
). Dies liegt daran , Unity Koroutinen ergeben Kontrolle ihrer Anrufer.yield
ing unterscheidet sich vonreturning
:return
Anweisung beendet die Ausführung einer Funktion, selbst wenn ihr mehr Code folgtyield
Anweisung unterbricht die Funktion und beginnt in der nächsten Zeile nach deryield
Wiederaufnahme.Normalerweise werden Coroutinen in jedem Frame fortgesetzt, aber Sie geben auch ein
WaitForSeconds
Objekt zurück.Die Zeile bedeutet
yield return new WaitForSeconds(fireTime)
grob übersetzt "Jetzt suspendiere mich und komme erst zurück, wennfireTime
Sekunden vergangen sind".Sofern nicht gestoppt, ist dies eine Coroutine, die nach dem Start die gesamte Schleife alle
fireTime
Sekunden ausführt .quelle
Eine einfache Erklärung: Unter der Haube iteriert Unity über eine Sammlung (von YieldInstruction s oder nulls oder was auch immer Sie
yield return
) mit derIEnumerator
, die Ihre Funktion zurückgibt.Da Sie das
yield
Schlüsselwort verwenden, ist Ihre Methode ein Iterator . Es ist nicht die Unity-Sache, es ist eine C # -Sprachenfunktion. Wie funktioniert es?Es ist faul und generiert nicht die gesamte Sammlung auf einmal (und die Sammlung kann unendlich sein und kann nicht auf einmal generiert werden). Elemente der Sammlung werden nach Bedarf generiert. Ihre Funktion gibt einen Iterator zurück, mit dem Unity arbeiten kann. Es ruft seine
MoveNext
Methode auf, um ein neues Element und eine neueCurrent
Eigenschaft für den Zugriff darauf zu generieren .Ihre Schleife ist also nicht endlos, sie führt Code aus, gibt ein Element zurück und gibt die Steuerung an Unity zurück, damit sie nicht hängen bleibt und andere Arbeiten ausführen kann, z. B. die Eingabe, um die Coroutine zu stoppen.
quelle
Überlegen Sie, wie ein
foreach
funktioniert:Die Kontrolle über die Iteration liegt beim Aufrufer - wenn Sie die Iteration stoppen (hier mit
break
), ist es das.Das
yield
Schlüsselwort ist eine einfache Möglichkeit, eine Aufzählung in C # zu erstellen. Der Name deutet darauf hin -yield return
gibt dem Anrufer (in diesem Fall unseremforeach
) die Kontrolle zurück ; Es ist der Anrufer, der entscheidet, wann mit dem nächsten Element fortgefahren werden soll. So können Sie eine Methode wie folgt erstellen:Das sieht naiv so aus, als würde es für immer laufen; In Wirklichkeit hängt es jedoch ganz vom Anrufer ab. Sie können so etwas tun:
Dies kann etwas verwirrend sein, wenn Sie nicht an dieses Konzept gewöhnt sind, aber ich hoffe, es ist auch offensichtlich, dass dies eine sehr nützliche Eigenschaft ist. Es war der einfachste Weg, Ihrem Anrufer die Kontrolle zu geben, und wenn der Anrufer beschließt, nachzufolgen, kann er nur den nächsten Schritt ausführen (wenn Unity heute erstellt wurde, würde es wahrscheinlich
await
anstelle von verwendenyield
, existierte aberawait
nicht zurück dann).Alles, was Sie brauchen, um Ihre eigenen Coroutinen zu implementieren (natürlich die einfachsten, dümmsten Coroutinen), ist Folgendes:
Um eine sehr einfache
WaitForSeconds
Implementierung hinzuzufügen , benötigen Sie lediglich Folgendes:Und der entsprechende Code in unserer Hauptschleife:
Ta-da - das ist alles, was ein einfaches Coroutine-System benötigt. Und indem er dem Anrufer die Kontrolle übergibt, kann der Anrufer über eine beliebige Anzahl von Dingen entscheiden. Sie haben möglicherweise eine sortierte Ereignistabelle, anstatt alle Coroutinen in jedem Frame zu durchlaufen. Sie können Prioritäten oder Abhängigkeiten haben. Es ermöglicht eine sehr einfache Implementierung von kooperativem Multitasking. Und schau dir an, wie einfach das ist, dank
yield
:)quelle