Dans le moteur de jeu Unity3D, une séquence de code commune pour obtenir des données à distance est la suivante:
WWW www = new WWW("http://remote.com/data/location/with/texture.png");
yield return www;
Quel est le mécanisme sous-jacent ici?
Je sais que nous utilisons le mécanisme de rendement afin de permettre le traitement de la prochaine image pendant le téléchargement. Mais que se passe-t-il sous le capot lorsque nous faisons le yield return www
?
Quelle méthode est appelée (le cas échéant, sur la classe WWW)? Unity utilise-t-il des threads? La couche Unity "supérieure" s'empare-t-elle de l'instance www et fait-elle quelque chose?
ÉDITER:
- Cette question concerne spécifiquement les composants internes d'Unity3D. Je ne suis pas intéressé par les explications sur le fonctionnement de l'
yield
instruction en C #. Au lieu de cela, je cherche une vue intérieure de la façon dont Unity traite ces constructions, pour permettre, par exemple, à WWW de télécharger une donnée de manière distribuée sur plusieurs trames.
yield return
pour les opérations asynchrones est un hack. Dans un "vrai" programme C #, vous utiliseriez unTask
pour cela. Unity ne les utilise probablement pas car il a été créé avant .Net 4.0, lors deTask
son introduction.Réponses:
Il s'agit du mot-clé C # yield en action - il ne fait rien de spécial avec l'
www
objet, il signifie plutôt quelque chose de spécial pour la méthode dans laquelle il est contenu. Plus précisément, ce mot-clé ne peut être utilisé que dans une méthode qui renvoie unIEnumerable
(ouIEnumerator
), et est utilisé pour indiquer quel objet sera "renvoyé" par l'énumérateur lorsque MoveNext est appelé.Cela fonctionne parce que le compilateur convertit la méthode entière en une classe distincte qui implémente
IEnumerable
(ouIEnumerator
) à l'aide d'une machine d'état - le résultat net est que le corps de la méthode elle-même n'est pas exécuté jusqu'à ce que quelqu'un énumère via la valeur de retour. Cela fonctionnera avec n'importe quel type, il n'y a absolument rien de spécialWWW
, c'est plutôt la méthode conteneur qui est spéciale.Jetez un œil aux coulisses du mot-clé de rendement C # pour en savoir plus sur le type de code généré par le compilateur C #, ou expérimentez et inspectez le code vous-même en utilisant quelque chose comme IL Spy
Mise à jour: pour clarifier
yield return
instruction, tout ce qui se passe est qu'un énumérateur est renvoyé - aucun des corps de méthode n'est exécuté à ce stadeMoveNext
l'itérateur afin d'obtenir la première valeur de la séquence. Cela provoque l'exécution de la méthode jusqu'à la premièreyeild return
instruction, moment auquel l'appelant reprend (et vraisemblablement Unity continue de rendre le reste de la trame)MoveNext
méthode sur l'itérateur une fois par trame suivante, ce qui fait que la méthode s'exécute à nouveau jusqu'à l'yield return
instruction suivante une fois par trame, jusqu'à ce que la fin de la méthode ou uneyield break
instruction soit atteinte (indiquant la fin de la séquence)Le bit spécial seulement ici (et dans deux ou trois d' autres cas ) est que l' unité ne fait pas avancer ce iterator particulier la trame suivante, au lieu , il avance que la iterator ( ce qui provoque la méthode pour poursuivre l' exécution) lorsque le téléchargement est terminé. Bien qu'il semble y avoir une classe YieldInstruction de base qui contient vraisemblablement un mécanisme générique pour signaler à Unity quand un itérateur doit être avancé, la
WWW
classe ne semble pas hériter de cette classe, donc je ne peux que supposer qu'il existe un cas spécial pour cette classe dans le moteur Unity.Juste pour être clair - le
yield
mot-clé ne fait rien de spécial pour laWWW
classe, c'est plutôt le traitement spécial que Unity donne aux membres de l'énumération retournée qui provoque ce comportement.Mettez à jour la seconde: en ce qui concerne le mécanisme qui
WWW
utilise pour télécharger les pages Web de manière asynchrone, il utilise probablement la méthode HttpWebRequest.BeginGetResponse qui utilisera en interne des E / S asynchrones ou bien il pourrait utiliser des threads (soit en créant un thread dédié, soit en utilisant un pool de threads).la source
WWW
objet quand il est cédé, voirWWW
la référence de .yield
semble être principalement utilisé dans Unity dans un contexte de coroutine. Pour en savoir plus sur les coroutines et pourquoi ils utilisent les C #,yield
je recommande cet article de blog: les coroutines Unity3D en détail . La plupart des recherches dans cette réponse proviennent de cet article.Les coroutines dans Unity sont utilisées pour encapsuler des tâches qui:
Des exemples de ces types de tâches sont les (re) calculs d'orientation ou, comme c'est le cas dans votre question, l'obtention de données à partir d'un site Web.
Pour répondre à vos sous-questions (dans un ordre légèrement modifié):
La
WWW
classe d'Unity est conçue pour être produite à partir d'une coroutine. Selon les commentaires sur l'article de blog lié ci-dessus, le bloc de code spéculatif (la couche "" supérieure ") surYieldInstruction
s contient en fait un commutateur qui vérifie également lesWWW
s produits. Ce code s'assure alors que la coroutine se terminera automatiquement une fois le téléchargement terminé, comme décrit dansWWW
la référence de .Dans ce cas, pour télécharger les données "sans bloquer le reste du jeu": oui, très probablement. (Et le filetage est définitivement utilisé pour décompresser les données téléchargées, comme en témoigne
WWW.threadPriority
.)la source
Malheureusement, WWW est implémenté en interne en tant que code natif, ce qui signifie que nous ne pouvons pas regarder le code. Par expérimentation, je peux dire que
WWW
n'est pas dérivé deYieldInstruction
, donc quoi qu'il arrive quand vousyield
devez le gérer par un code de cas spécial.Je n'ai jamais observé de différence entre
et
Je pense que c'est la façon la plus logique de le mettre en œuvre, et c'est probablement ce qui se passe sous le capot. Mais je n'en suis pas sûr.
Unity ne démarre pas de nouveau thread à télécharger, du moins sur certaines plateformes (iOS, webplayer). Ou si c'est le cas, il se place
WWW.isDone
sur le thread principal. Je le sais parce que ce code:ne fonctionne pas.
Je ne pense pas que vous puissiez avoir des réponses plus spécifiques à moins qu'une personne ayant accès au code source d'Unity3d vienne ici.
la source
Comme Unity3D utilise C # comme moteur de script, je suppose que c'est le mot-clé de rendement standard intégré à C #. Fondamentalement, cela signifie qu'il renvoie déjà la valeur de www afin que vous puissiez continuer tandis que la prochaine itération, il retournera la valeur suivante, etc ... Le rendement crée essentiellement une machine à états et un itérateur en arrière-plan.
la source
WWW
référence . De plus, je ne suis pas sûr deyield
créer quoi que ce soit. Le contexte d'itérateur est créé en l'implémentantIEnumerable
ou en l'utilisant comme type de retour. "State machine" semble également éteint. Bien sûr, il y a un État, mais cela seul n'est pas une propriété suffisante, non? Peut-être pourriez-vous nous en dire plus.