Je comprends le principe des coroutines. Je sais comment faire fonctionner le standard StartCoroutine
/ yield return
pattern en C # dans Unity, par exemple invoquer une méthode retournant IEnumerator
via StartCoroutine
et dans cette méthode faire quelque chose, yield return new WaitForSeconds(1);
attendre une seconde, puis faire autre chose.
Ma question est: que se passe-t-il vraiment dans les coulisses? Que fait StartCoroutine
vraiment? Qu'est IEnumerator
- ce que le WaitForSeconds
retour? Comment StartCoroutine
retourne le contrôle à la partie «autre chose» de la méthode appelée? Comment tout cela interagit-il avec le modèle de concurrence d'Unity (où beaucoup de choses se passent en même temps sans utiliser de coroutines)?
IEnumerator
/IEnumerable
(ou les équivalents génériques) et qui contiennent leyield
mot - clé. Recherchez les itérateurs.Réponses:
Le lien de détail des coroutines Unity3D souvent référencé est mort. Puisqu'il est mentionné dans les commentaires et les réponses, je vais publier le contenu de l'article ici. Ce contenu provient de ce miroir .
la source
Le premier titre ci-dessous est une réponse directe à la question. Les deux titres suivants sont plus utiles pour le programmeur de tous les jours.
Détails de mise en œuvre peut-être ennuyeux de Coroutines
Les coroutines sont expliquées sur Wikipedia et ailleurs. Ici, je vais simplement fournir quelques détails d'un point de vue pratique.
IEnumerator
,yield
Etc. sont C # caractéristiques linguistiques qui sont utilisés pour un peu d'un but différent dans l' unité.Pour le dire très simplement, an
IEnumerator
prétend avoir une collection de valeurs que vous pouvez demander une par une, un peu comme unList
. En C #, une fonction avec une signature pour renvoyer unIEnumerator
n'a pas besoin d'en créer et d'en renvoyer une, mais peut laisser C # fournir un impliciteIEnumerator
. La fonction peut alors fournir le contenu de celui retournéIEnumerator
dans le futur de manière paresseuse, par le biais d'yield return
instructions. Chaque fois que l'appelant demande une autre valeur à partir de cet impliciteIEnumerator
, la fonction s'exécute jusqu'à l'yield return
instruction suivante , qui fournit la valeur suivante. En tant que sous-produit de cela, la fonction s'arrête jusqu'à ce que la valeur suivante soit demandée.Dans Unity, nous ne les utilisons pas pour fournir des valeurs futures, nous exploitons le fait que la fonction s'arrête. A cause de cette exploitation, beaucoup de choses sur les coroutines dans Unity n'ont pas de sens (Qu'est-ce qui
IEnumerator
a à voir avec quoi que ce soit? Qu'est-ce queyield
? Pourquoinew WaitForSeconds(3)
? Etc.). Ce qui se passe "sous le capot", c'est que les valeurs que vous fournissez via IEnumerator sont utilisées parStartCoroutine()
pour décider quand demander la valeur suivante, qui détermine quand votre coroutine se relancera.Votre jeu Unity est à un seul thread (*)
Les coroutines ne sont pas des threads. Il y a une boucle principale d'Unity et toutes ces fonctions que vous écrivez sont appelées par le même thread principal dans l'ordre. Vous pouvez le vérifier en plaçant un
while(true);
dans l'une de vos fonctions ou coroutines. Cela gèlera le tout, même l'éditeur Unity. C'est la preuve que tout fonctionne dans un thread principal. Ce lien mentionné par Kay dans son commentaire ci-dessus est également une excellente ressource.(*) Unity appelle vos fonctions à partir d'un thread. Ainsi, à moins que vous ne créiez vous-même un thread, le code que vous avez écrit est à thread unique. Bien sûr, Unity utilise d'autres threads et vous pouvez créer des threads vous-même si vous le souhaitez.
Une description pratique des coroutines pour les programmeurs de jeux
En fait, lorsque vous appelez
StartCoroutine(MyCoroutine())
, il est exactement comme un appel de fonction régulièreMyCoroutine()
, jusqu'à ce que le premieryield return X
, oùX
est quelque chose commenull
,new WaitForSeconds(3)
,StartCoroutine(AnotherCoroutine())
,break
, etc. C'est quand il commence à partir d' une fonction différente. Unity "met en pause" cette fonction juste à cetteyield return X
ligne, continue avec d'autres activités et certaines trames passent, et quand il est à nouveau temps, Unity reprend cette fonction juste après cette ligne. Il mémorise les valeurs de toutes les variables locales de la fonction. De cette façon, vous pouvez avoir unefor
boucle qui boucle toutes les deux secondes, par exemple.Le moment où Unity reprendra votre coroutine dépend de ce qu'il y
X
avait dans votre fichieryield return X
. Par exemple, si vous avez utiliséyield return new WaitForSeconds(3);
, il reprend après 3 secondes. Si vous l'avez utiliséyield return StartCoroutine(AnotherCoroutine())
, il reprend une foisAnotherCoroutine()
terminé, ce qui vous permet d'imbriquer les comportements dans le temps. Si vous venez d'utiliser ayield return null;
, il reprend directement à l'image suivante.la source
Cela ne pourrait pas être plus simple:
Unity (et tous les moteurs de jeu) sont basés sur des cadres .
Le point entier, la raison d'être de l'Unity, c'est qu'elle est basée sur un cadre. Le moteur fait les choses «à chaque image» pour vous. (Anime, rend des objets, fait de la physique, etc.)
Vous pourriez demander… "Oh, c'est génial. Et si je veux que le moteur fasse quelque chose pour moi à chaque image? Comment dire au moteur de faire telle ou telle chose dans un cadre?"
La réponse est ...
C'est exactement à cela que sert une "coroutine".
C'est aussi simple que cela.
Et considérez ceci ...
Vous connaissez la fonction "Mettre à jour". Tout simplement, tout ce que vous y mettez est fait à chaque image . C'est littéralement exactement la même chose, pas de différence du tout, de la syntaxe coroutine-yield.
Il n'y a absolument aucune différence.
Note de bas de page: comme tout le monde l'a souligné, Unity n'a tout simplement aucun fil . Les "frames" dans Unity ou dans n'importe quel moteur de jeu n'ont absolument aucun lien avec les threads.
Les coroutines / yield sont simplement la façon dont vous accédez aux cadres dans Unity. C'est tout. (Et en effet, c'est absolument la même chose que la fonction Update () fournie par Unity.) C'est tout ce qu'il y a à faire, c'est aussi simple que cela.
la source
Update()
? Je veux dire qu'il devrait y avoir au moins une légère différence entre ces deux implémentations et leurs cas d'utilisation, ce qui est assez évident.J'ai creusé cela récemment, j'ai écrit un article ici - http://eppz.eu/blog/understanding-ienumerator-in-unity-3d/ - qui jette un éclairage sur les éléments internes (avec des exemples de code denses), l'
IEnumerator
interface sous-jacente , et comment il est utilisé pour les coroutines.la source
Les fonctions de base dans Unity que vous obtenez automatiquement sont la fonction Start () et la fonction Update (), donc celles de Coroutine sont essentiellement des fonctions tout comme les fonctions Start () et Update (). Toute ancienne fonction func () peut être appelée de la même manière qu'une Coroutine peut être appelée. Unity a évidemment défini certaines limites pour les Coroutines qui les différencient des fonctions régulières. Une différence est au lieu de
vous écrivez
pour les coroutines. Et de la même manière, vous pouvez contrôler l'heure dans les fonctions normales avec des lignes de code comme
Une coroutine a une poignée spécifique sur la façon dont le temps peut être contrôlé.
Bien que ce ne soit pas la seule chose possible à faire à l'intérieur d'un IEnumerator / Coroutine, c'est l'une des choses utiles pour lesquelles les Coroutines sont utilisées. Vous devrez rechercher l'API de script d'Unity pour apprendre d'autres utilisations spécifiques de Coroutines.
la source
StartCoroutine est une méthode pour appeler une fonction IEnumerator. C'est similaire à simplement appeler une simple fonction void, la différence est que vous l'utilisez sur les fonctions IEnumerator. Ce type de fonction est unique car il peut vous permettre d'utiliser une fonction de rendement spéciale , notez que vous devez retourner quelque chose. C'est pour autant que je sache. Ici, j'ai écrit un jeu de scintillement simple sur la méthode du texte dans l'unité
Je l'ai ensuite appelé hors du IEnumerator lui-même
Comme vous pouvez voir comment j'ai utilisé la méthode StartCoroutine (). J'espère que j'ai aidé d'une manière ou d'une autre. Je suis moi-même un débutant, donc si vous me corrigez ou m'appréciez, n'importe quel type de rétroaction serait formidable.
la source