Recherche…


Syntaxe

  • Coroutine StartCoroutine publique (routine IEnumerator);
  • public Coroutine StartCoroutine (string methodName, valeur d'objet = null);
  • StopCoroutine publique vide (string methodName);
  • StopCoroutine publique annulée (routine IEnumerator);
  • annulation publique StopAllCoroutines ();

Remarques

Considérations de performance

Il est préférable d'utiliser les coroutines avec modération, car la flexibilité entraîne un coût de performance.

  • Coroutines en grand nombre exige plus de la part du processeur que les méthodes de mise à jour standard.
  • Il y a un problème dans certaines versions d'Unity où les coroutines produisent des ordures à chaque cycle de mise à jour en raison de la boîte Unity MoveNext valeur de retour MoveNext . Cela a été observé pour la dernière fois en 5.4.0b13. ( Rapport de bogue )

Réduisez les erreurs en mettant en cache YieldInstructions

Une astuce courante pour réduire les déchets générés dans les coroutines consiste à mettre en cache l’ YieldInstruction .

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

Le fait de produire null ne produit pas de déchets supplémentaires.

Coroutines

Tout d'abord, il est essentiel de comprendre que les moteurs de jeu (tels que Unity) fonctionnent selon un paradigme basé sur les images.

Le code est exécuté à chaque image.

Cela inclut le code propre à Unity et votre code.

Lorsque l'on pense aux cadres, il est important de comprendre qu'il n'y a absolument aucune garantie que les images se produisent. Ils ne se produisent pas sur un rythme régulier. Les écarts entre les images peuvent être, par exemple, 0,02632 puis 0,021167 puis 0,029778, etc. Dans l'exemple, ils sont tous "environ" 1/50 de seconde, mais ils sont tous différents. Et à tout moment, vous pouvez obtenir un cadre beaucoup plus long ou plus court. et votre code peut être exécuté à tout moment dans le cadre.

Gardant cela à l'esprit, vous pouvez vous demander: comment accéder à ces cadres dans votre code, dans Unity?

Tout simplement, vous utilisez soit l’appel Update (), soit vous utilisez une coroutine. (En effet - ils sont exactement la même chose: ils permettent d'exécuter le code à chaque image.)

Le but d'une coroutine est que:

vous pouvez exécuter du code, puis "arrêtez et attendez" jusqu'à une future image .

Vous pouvez attendre jusqu'à la prochaine image , vous pouvez attendre un certain nombre d'images ou vous pouvez attendre un temps approximatif en secondes dans le futur.

Par exemple, vous pouvez attendre "environ une seconde", ce qui signifie qu'il faudra attendre environ une seconde, puis placer votre code dans une image à peu près une seconde. (Et en effet, dans ce cadre, le code pourrait être exécuté à tout moment, quel qu’il soit.) Répéter: ce ne sera pas exactement une seconde. Un timing précis n'a pas de sens dans un moteur de jeu.

Dans une coroutine:

Attendre un cadre:

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

Attendre trois images:

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

Attendre environ une demi-seconde:

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

Faites quelque chose à chaque image:

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

Cet exemple est littéralement identique à mettre simplement quelque chose à l'intérieur de l'appel "Mise à jour" d'Unity: le code à "faire quelque chose" est exécuté à chaque image.

Exemple

Attachez Ticker à un GameObject . Tant que cet objet de jeu est actif, le tick sera exécuté. Notez que le script arrête soigneusement la coroutine lorsque l'objet de jeu devient inactif; c'est généralement un aspect important de l'ingénierie correcte de l'utilisation des coroutines.

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
        }
    }
}

Mettre fin à une coroutine

Souvent, vous concevez des coroutines qui se terminent naturellement lorsque certains objectifs sont atteints.

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");
}

Pour arrêter une coroutine de "l'intérieur" de la coroutine, vous ne pouvez pas simplement "revenir" comme vous le feriez plus tôt d'une fonction ordinaire. Au lieu de cela, vous utilisez la yield break .

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

Vous pouvez également forcer toutes les coroutines lancées par le script à s'arrêter avant de terminer.

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

La méthode pour arrêter une coroutine spécifique à partir de l'appelant varie selon la manière dont vous l'avez démarrée.

Si vous avez démarré une coroutine par nom de chaîne:

StartCoroutine("YourAnimation");

alors vous pouvez l'arrêter en appelant StopCoroutine avec le même nom de chaîne:

StopCoroutine("YourAnimation");

Vous pouvez également conserver une référence à la IEnumerator renvoyée par la méthode coroutine, ou l' Coroutine objet retourné par StartCoroutine et appeler StopCoroutine sur l' un de ceux -ci :

public class SomeComponent : MonoBehaviour 
{
    Coroutine routine;

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

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

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

Méthodes MonoBehaviour qui peuvent être Coroutines

Il existe trois méthodes MonoBehaviour qui peuvent être réalisées en coroutines.

  1. Début()
  2. OnBecameVisible ()
  3. OnLevelWasLoaded ()

Cela peut être utilisé pour créer, par exemple, des scripts qui ne s'exécutent que lorsque l'objet est visible pour une caméra.

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();
    }
}

Enchaînement de coroutines

Les coroutines peuvent céder à l'intérieur d'elles-mêmes et attendre d' autres coroutines .

Vous pouvez donc enchaîner les séquences - "l'une après l'autre".

C'est très simple, et c'est une technique de base dans l'Unité.

Dans les jeux, il est absolument naturel que certaines choses se passent "dans l'ordre". Presque chaque "round" d'un jeu commence par une série d'événements se produisant, dans un certain temps, dans un certain ordre. Voici comment commencer un jeu de course automobile:

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

Donc, quand vous appelez BeginRace ...

 StartCoroutine(BeginRace());

Il exécutera votre routine "préparer la course". (Peut-être, en faisant clignoter des lumières et en faisant du bruit sur la foule, en réinitialisant les scores, etc.). Lorsque cela sera terminé, votre séquence "Compte à rebours" sera animée, vous permettant d'animer un compte à rebours sur l'interface utilisateur. Lorsque cela est terminé, il lancera votre code de départ de la course, où vous pourrez peut-être exécuter des effets sonores, démarrer des pilotes d’IA, déplacer l’appareil d’une certaine manière, etc.

Pour plus de clarté, comprenez que les trois appels

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

doivent eux-mêmes être dans une coroutine. C'est-à-dire qu'ils doivent être dans une fonction du type IEnumerator . Donc, dans notre exemple, c'est IEnumerator BeginRace . Ainsi, à partir du code "normal", vous lancez cette coroutine avec l'appel StartCoroutine .

 StartCoroutine(BeginRace());

Pour mieux comprendre le chaînage, voici une fonction qui enchaîne les coroutines. Vous passez dans un tableau de coroutines. La fonction exécute autant de coroutines que vous passez, dans l'ordre, l'une après l'autre.

// 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;
}

Voici comment vous appelez cela ... disons que vous avez trois fonctions. Rappelez-vous qu'ils doivent tous être 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;
}

Tu appellerais ça comme ça

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

ou peut-être comme ça

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

Pour répéter, l'une des exigences les plus fondamentales des jeux est que certaines choses se produisent les unes après les autres "dans un ordre" au fil du temps. Vous réalisez cela dans l'unité très simplement, avec

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

Façons de céder

Vous pouvez attendre jusqu'à la prochaine image.

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

Vous pouvez avoir plusieurs de ces appels d'affilée, pour simplement attendre autant d'images que vous le souhaitez.

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

Attendez environ n secondes. Il est extrêmement important de comprendre que ce n’est qu’un temps très approximatif .

yield return new WaitForSeconds(n);

Il n'est absolument pas possible d'utiliser l'appel "WaitForSeconds" pour toute forme de synchronisation précise.

Souvent, vous voulez enchaîner les actions. Alors, faites quelque chose, et quand c'est fini, faites autre chose et quand c'est fini, faites autre chose. Pour cela, attendez une autre coroutine:

yield return StartCoroutine(coroutine);

Comprenez que vous ne pouvez l'appeler que dans une coroutine. Alors:

StartCoroutine(Test());

C'est comme ça que vous commencez une coroutine à partir d'un morceau de code "normal".

Puis, dans cette coroutine en cours d'exécution:

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

Cela imprimera A, démarrera le long processus et imprimera immédiatement B. Il n'attendra pas que le long processus se termine. D'autre part:

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

Cela imprimera A, lancera le long processus, attendra la fin du processus, puis imprimera B.

Il est toujours utile de se rappeler que les coroutines n'ont absolument aucun lien avec le threading. Avec ce code:

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

il est facile de penser que c'est comme "démarrer" le LongProcess sur un autre thread en arrière-plan. Mais c'est absolument incorrect. C'est juste une coroutine. Les moteurs de jeux sont basés sur des cadres et les "coroutines" dans Unity vous permettent simplement d'accéder aux images.

Il est très facile d'attendre la fin d'une requête Web.

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);
    }
}

Pour être complet: dans de très rares cas, vous utilisez une mise à jour fixe dans Unity; il y a un appel WaitForFixedUpdate() qui normalement ne serait jamais utilisé. Il existe un appel spécifique ( WaitForEndOfFrame() dans la version actuelle d'Unity) qui est utilisé dans certaines situations pour générer des captures d'écran au cours du développement. (Le mécanisme exact change légèrement au fur et à mesure que l’Unity évolue, donc google pour les dernières informations s’il ya lieu.)



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow