Suche…


Syntax

  • public Coroutine StartCoroutine (Routine IEnumerator);
  • public Coroutine StartCoroutine (String methodName, Objektwert = null);
  • public void StopCoroutine (Zeichenfolge Methodenname);
  • public void StopCoroutine (Routine IEnumerator);
  • public void StopAllCoroutines ();

Bemerkungen

Überlegungen zur Leistung

Es ist am besten, Coroutines in Maßen zu verwenden, da die Flexibilität mit Leistungskosten einhergeht.

  • Coroutines verlangen in großer Zahl mehr von der CPU als von Standard-Update-Methoden.
  • In einigen Unity-Versionen gibt es ein Problem, bei dem Coroutines bei jedem Aktualisierungszyklus Müll erzeugen, da Unity den MoveNext Rückgabewert MoveNext . Dies wurde zuletzt in 5.4.0b13 beobachtet. ( Fehlerbericht )

Reduzieren Sie den Müll durch Zwischenspeichern von YieldInstructions

Ein üblicher Trick, um den in Coroutinen erzeugten Müll zu reduzieren, besteht im YieldInstruction der YieldInstruction .

IEnumerator TickEverySecond()
{
    var wait = new WaitForSeconds(1f); // Cache
    while(true)
    {
        yield return wait; // Reuse
    }
}

Das Nachgeben von null erzeugt keinen zusätzlichen Müll.

Coroutinen

Zunächst ist es wichtig zu verstehen, dass Spiel-Engines (wie Unity) nach einem "Frame-basierten" Paradigma arbeiten.

Code wird bei jedem Frame ausgeführt.

Dazu gehört der eigene Code von Unity und Ihr Code.

Beim Nachdenken über Frames ist es wichtig zu verstehen, dass es absolut keine Garantie dafür gibt, wann Frames auftreten. Sie passieren nicht regelmäßig. Die Lücken zwischen Frames könnten zum Beispiel 0,02632, dann 0,021167, dann 0,029778 usw. sein. In dem Beispiel sind sie alle etwa 1/50 einer Sekunde, aber sie sind alle unterschiedlich. Und Sie erhalten zu jeder Zeit einen Frame, der viel länger dauert oder kürzer ist. und Ihr Code kann jederzeit innerhalb des Rahmens ausgeführt werden.

In Anbetracht dessen fragen Sie vielleicht: Wie greifen Sie auf diese Frames in Ihrem Code zu, in Unity?

Sie verwenden ganz einfach entweder den Aufruf Update () oder eine Coroutine. (In der Tat - sie sind genau das Gleiche: Sie ermöglichen, dass Code für jeden Frame ausgeführt wird.)

Der Zweck einer Coroutine ist:

Sie können etwas Code ausführen und dann "stop and wait" bis zu einem zukünftigen Frame .

Sie können bis zum nächsten Frame warten, können Sie für eine Reihe von Frames warten, oder Sie können für einige ungefähre Zeit in Sekunden in der Zukunft warten.

Zum Beispiel können Sie auf "etwa eine Sekunde" warten, was bedeutet, dass es etwa eine Sekunde warten wird, und dann den Code in etwa einer Sekunde in einen Frame stellen. (Und tatsächlich kann der Code innerhalb dieses Rahmens jederzeit ausgeführt werden.) Wiederholen: Es wird nicht genau eine Sekunde dauern. Genaues Timing ist in einer Spiel-Engine ohne Bedeutung.

In einer Coroutine:

Einen Frame warten:

// do something
yield return null;  // wait until next frame
// do something

Drei Frames warten:

// do something
yield return null;  // wait until three frames from now
yield return null;
yield return null;
// do something

Um etwa eine halbe Sekunde zu warten:

// do something
yield return new WaitForSeconds (0.5f); // wait for a frame in about .5 seconds
// do something

Mach jeden einzelnen Frame etwas:

while (true)
{
    // do something
    yield return null;  // wait until the next frame
}

Dieses Beispiel ist buchstäblich identisch mit dem Einfügen von etwas in Unitys "Update" -Aufruf: Der Code bei "etwas tun" wird bei jedem Frame ausgeführt.

Beispiel

Ticker an ein GameObject . Während dieses Spielobjekt aktiv ist, wird der Haken ausgeführt. Beachten Sie, dass das Skript die Coroutine vorsichtig stoppt, wenn das Spielobjekt inaktiv wird. Dies ist normalerweise ein wichtiger Aspekt für die korrekte Verwendung der Coroutinen.

using UnityEngine;
using System.Collections;

public class Ticker:MonoBehaviour {

    void OnEnable()
    {
        StartCoroutine(TickEverySecond());
    }

    void OnDisable()
    {
        StopAllCoroutines();
    }

    IEnumerator TickEverySecond()
    {
        var wait = new WaitForSeconds(1f); // REMEMBER: IT IS ONLY APPROXIMATE
        while(true)
        {
            Debug.Log("Tick");
            yield return wait;  // wait for a frame, about 1 second from now
        }
    }
}

Eine Coroutine beenden

Oft entwerfen Sie Coroutinen so, dass sie auf natürliche Weise enden, wenn bestimmte Ziele erreicht werden.

IEnumerator TickFiveSeconds()
{
    var wait = new WaitForSeconds(1f);
    int counter = 1;
    while(counter < 5)
    {
        Debug.Log("Tick");
        counter++;
        yield return wait;
    }
    Debug.Log("I am done ticking");
}

Um zu verhindern, dass eine Coroutine "innerhalb" der Coroutine "steht", können Sie nicht einfach "zurückkehren", als würden Sie eine gewöhnliche Funktion früher verlassen. Stattdessen verwenden Sie den yield break .

IEnumerator ShowExplosions()
{
    ... show basic explosions
    if(player.xp < 100) yield break;
    ... show fancy explosions
}

Sie können auch alle vom Skript gestarteten Coroutinen zwingen, vor dem Beenden anzuhalten.

void OnDisable()
{
    // Stops all running coroutines
    StopAllCoroutines();
}

Die Methode, um eine bestimmte Coroutine vom Anrufer zu stoppen, hängt davon ab, wie Sie sie gestartet haben.

Wenn Sie eine Coroutine mit dem Namen der Zeichenfolge gestartet haben:

StartCoroutine("YourAnimation");

Dann können Sie es stoppen, indem Sie StopCoroutine mit demselben String-Namen aufrufen:

StopCoroutine("YourAnimation");

Alternativ können Sie einen Verweis halten , um entweder die IEnumerator durch die Koroutine Verfahren zurückgegeben wird , oder das Coroutine Objekt durch zurück StartCoroutine und rufen StopCoroutine auf eine der beiden:

public class SomeComponent : MonoBehaviour 
{
    Coroutine routine;

    void Start () {
        routine = StartCoroutine(YourAnimation());
    }

    void Update () {
        // later, in response to some input...
        StopCoroutine(routine);
    }

    IEnumerator YourAnimation () { /* ... */ }
}

MonoBehaviour-Methoden, die Coroutines sein können

Es gibt drei MonoBehaviour-Methoden, die zu Coroutinen gemacht werden können.

  1. Start()
  2. OnBecameVisible ()
  3. OnLevelWasLoaded ()

Damit können Sie beispielsweise Skripts erstellen, die nur ausgeführt werden, wenn das Objekt für eine Kamera sichtbar ist.

using UnityEngine;
using System.Collections;

public class RotateObject : MonoBehaviour
{
    IEnumerator OnBecameVisible()
    {
        var tr = GetComponent<Transform>();
        while (true)
        {
            tr.Rotate(new Vector3(0, 180f * Time.deltaTime));
            yield return null;
        }
    }
    
    void OnBecameInvisible()
    {
        StopAllCoroutines();
    }
}

Verketten von Coroutinen

Coroutinen können in sich nachgeben und auf andere Coroutinen warten.

Sie können also Sequenzen verketten - "eine nach der anderen".

Dies ist sehr einfach und eine grundlegende Technik in Unity.

In Spielen ist es absolut selbstverständlich, dass bestimmte Dinge "in Ordnung" passieren müssen. Fast jede "Runde" eines Spiels beginnt mit einer bestimmten Reihe von Ereignissen über einen bestimmten Zeitraum und in einer bestimmten Reihenfolge. So starten Sie ein Autorennen:

IEnumerator BeginRace()
{
  yield return StartCoroutine(PrepareRace());
  yield return StartCoroutine(Countdown());
  yield return StartCoroutine(StartRace());
}

Wenn Sie also BeginRace anrufen ...

 StartCoroutine(BeginRace());

Es wird Ihre Routine "Vorbereitungsrennen" ausführen. (Vielleicht blinken einige Lichter und ein paar Menschengeräusche, Zurücksetzen von Ergebnissen usw.) Wenn dies beendet ist, wird der Countdown ausgeführt, in dem Sie möglicherweise einen Countdown auf der Benutzeroberfläche animieren. Wenn dies abgeschlossen ist, wird der Startcode für das Rennen ausgeführt, bei dem Sie möglicherweise Soundeffekte ausführen, einige KI-Treiber starten, die Kamera auf eine bestimmte Weise bewegen und so weiter.

Verstehen Sie zur Klarheit, dass die drei Aufrufe

  yield return StartCoroutine(PrepareRace());
  yield return StartCoroutine(Countdown());
  yield return StartCoroutine(StartRace());

muss selbst in einer Coroutine sein. Das heißt, sie müssen vom Typ IEnumerator . In unserem Beispiel ist das also IEnumerator BeginRace . Aus "normalem" Code starten Sie diese Coroutine mit dem Aufruf " StartCoroutine .

 StartCoroutine(BeginRace());

Um die Verkettung besser zu verstehen, haben wir hier eine Funktion, die Coroutinen verkettet. Sie übergeben eine Reihe von Coroutinen. Die Funktion führt so viele Coroutinen aus, wie Sie nacheinander durchlaufen.

// run various routines, one after the other
IEnumerator OneAfterTheOther( params IEnumerator[] routines ) 
{
    foreach ( var item in routines ) 
    {
        while ( item.MoveNext() ) yield return item.Current;
    }

    yield break;
}

So würden Sie das nennen ... Nehmen wir an, Sie haben drei Funktionen. Erinnern Sie sich, dass sie alle IEnumerator sein IEnumerator :

IEnumerator PrepareRace() 
{
    // codesay, crowd cheering and camera pan around the stadium
    yield break;
}

IEnumerator Countdown() 
{
    // codesay, animate your countdown on UI
    yield break;
}

IEnumerator StartRace() 
{
    // codesay, camera moves and light changes and launch the AIs
    yield break;
}

Du würdest es so nennen

StartCoroutine( MultipleRoutines( PrepareRace(), Countdown(), StartRace() ) );

oder vielleicht so

IEnumerator[] routines = new IEnumerator[] {
     PrepareRace(),
     Countdown(),
     StartRace() };
StartCoroutine( MultipleRoutines( routines ) );

Um es zu wiederholen, eine der grundlegendsten Anforderungen in Spielen ist, dass bestimmte Dinge "in einer Sequenz" im Laufe der Zeit nacheinander passieren. Das erreichen Sie in Unity sehr einfach mit

  yield return StartCoroutine(PrepareRace());
  yield return StartCoroutine(Countdown());
  yield return StartCoroutine(StartRace());

Wege zum Nachgeben

Sie können bis zum nächsten Frame warten.

yield return null; // wait until sometime in the next frame

Sie können mehrere dieser Anrufe in einer Reihe haben, um einfach auf beliebig viele Frames zu warten.

//wait for a few frames
yield return null;
yield return null;

Warten Sie ungefähr n Sekunden. Es ist äußerst wichtig zu verstehen, dass dies nur eine ungefähre Zeit ist .

yield return new WaitForSeconds(n);

Es ist absolut nicht möglich, den Aufruf "WaitForSeconds" für genaue Timing-Zeiten zu verwenden.

Oft möchten Sie Aktionen verketten. Also, mach etwas, und wenn das fertig ist, mach etwas anderes, und wenn das fertig ist, mach etwas anderes. Um dies zu erreichen, warten Sie auf eine andere Coroutine:

yield return StartCoroutine(coroutine);

Verstehen Sie, dass Sie dies nur von einer Coroutine aus aufrufen können. So:

StartCoroutine(Test());

So beginnen Sie eine Coroutine mit einem "normalen" Code.

Dann in dieser laufenden Coroutine:

Debug.Log("A");
StartCoroutine(LongProcess());
Debug.Log("B");

Dadurch wird A gedruckt, der lange Prozess gestartet und sofort B gedruckt . Es wird nicht warten, bis der lange Prozess abgeschlossen ist. Auf der anderen Seite:

Debug.Log("A");
yield return StartCoroutine(LongProcess());
Debug.Log("B");

Dann wird A gedruckt, der lange Vorgang gestartet, gewartet, bis der Vorgang abgeschlossen ist , und dann wird B gedruckt.

Es lohnt sich immer daran zu erinnern, dass Coroutinen keinerlei Verbindung zu Threads haben. Mit diesem Code:

Debug.Log("A");
StartCoroutine(LongProcess());
Debug.Log("B");

Man kann sich leicht vorstellen, dass es so ist, als würde man LongProcess in einem anderen Thread im Hintergrund starten. Das ist aber absolut falsch. Es ist nur eine Coroutine. Game Engines basieren auf Frames und "Coroutines" in Unity ermöglichen Ihnen einfach den Zugriff auf die Frames.

Es ist sehr einfach zu warten, bis eine Webanfrage abgeschlossen ist.

void Start() {
    string url = "http://google.com";
    WWW www = new WWW(url);
    StartCoroutine(WaitForRequest(www));
}

IEnumerator WaitForRequest(WWW www) {
    yield return www;
    
    if (www.error == null) {
        //use www.data);
    }
    else {
        //use www.error);
    }
}

Der Vollständigkeit halber: In sehr seltenen Fällen verwenden Sie ein festes Update in Unity. Es gibt einen WaitForFixedUpdate() Aufruf, der normalerweise niemals verwendet würde. Es gibt einen bestimmten Aufruf ( WaitForEndOfFrame() in der aktuellen Version von Unity), der in bestimmten Situationen beim WaitForEndOfFrame() von Bildschirmaufnahmen während der Entwicklung verwendet wird. (Der genaue Mechanismus ändert sich mit der Entwicklung von Unity geringfügig, also google nach den neuesten Informationen, falls relevant.)



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow