Je suis nouveau sur Unity. J'apprenais des coroutines et j'ai écrit cela.
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);
}
}
Lorsque le bouton est enfoncé, la coroutine est appelée et elle entre dans la boucle "while". Lorsque je quitte le bouton, cela arrête la coroutine. Ne devrait-il pas rester coincé dans la boucle «while» car c'est une boucle infinie? Pourquoi?
unity
c#
coroutines
cerveau de bébé
la source
la source
"Fire1"
, est-ce quelque chose que vous pouvez configurer dans le moteur pour permettre les remappages de touches plutôt que de taperKeycode.Foo
?yield
s'agit effectivement de «contrôle du rendement pour l'appelant jusqu'à ce que l'élément suivant dans l'énumérable soit demandé».StopAllCoroutines()
dans ce cas. C'est bien quand vous n'utilisez qu'une seule coroutine, mais si vous prévoyez d'en avoir plus d'une, cela aurait des effets indésirables. Au lieu de cela, vous devez utiliserStopCoroutine()
et simplement arrêter celui qui est pertinent au lieu de tous. (StopAllCoroutines()
serait utile par exemple pour terminer le niveau ou charger une nouvelle zone, etc., mais pas pour des choses spécifiques comme "Je ne tire plus".)Réponses:
La raison en est le mot-clé
yield
qui a une signification spécifique en C #.En rencontrant les mots,
yield return
une fonction en C # revient, comme on pourrait s'y attendre.Il n'y a donc pas de boucle infinie. Il existe une fonction / itérateur qui peut être appelée un nombre infini de fois.
La fonction Unity
StartCoroutine()
oblige le framework Unity à appeler la fonction / itérateur une fois par trame.La fonction
StopAllCoroutines
Unity empêche le framework Unity d'appeler la fonction / itérateur.Et le retour
WaitForSeconds(time)
de l'itérateur fait que le framework Unity suspend l'appel de la fonction / itérateur pourtime
.Un commentaire confus et un vote positif tout aussi confus sur ce commentaire m'ont encouragé à approfondir ce que le mot
yield
- clé fait et ne fait pas.Si vous écrivez ceci:
Vous pouvez également écrire ceci:
Il s'ensuit que le mot
yield
- clé n'est pas lié au multi-threading et n'appelle absolument pasSystem.Threading.Thread.Yield()
.la source
On encountering the words yield return a function in C# returns
". Non. Le texte que vous citez l'explique, tout comme 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.
". Fondamentalement, «s'il vous plaît, faites-moi une pause où je suis et laissez quelqu'un d'autre courir pendant un certain temps».Lorsque le bouton de tir est levé, la deuxième instruction if est entrée et StopAllCoroutines est exécuté. Cela signifie que la Coroutine dans laquelle la boucle while s'exécute est terminée, il n'y a donc plus de boucle infinie. La coroutine est comme un conteneur pour le code à exécuter.
Je peux recommander le manuel Unity et l' API Unity Scripting pour mieux comprendre ce que sont les coroutines et leur puissance.
Ce blog et cette recherche sur YouTube m'ont également aidé à mieux utiliser les coroutines.
la source
Les coroutines sont une étrange bête. Le retour de rendement entraîne la suspension de l'exécution de la méthode jusqu'à ce qu'elle soit ultérieurement modifiée. Dans les coulisses, cela pourrait ressembler à ceci:
Et interne à Unity / C # (puisque return yield est une fonctionnalité native c #), lorsque vous appelez StartCoroutine, il crée un
FireContinuouslyData
objet et le transmet à la méthode. En fonction de la valeur renvoyée, il détermine quand l'appeler à nouveau plus tard, stockant simplement l'objet FireContinuouslyData pour le transmettre la prochaine fois.Si vous avez déjà fait une pause de rendement, il pourrait simplement être défini en interne
data.shouldBreak = true
, puis Unity jetterait simplement les données et ne les planifierait plus.Et s'il y avait des données qui devaient être enregistrées entre les exécutions, elles seraient également stockées dans les données pour plus tard.
Un exemple de la façon dont Unity / C # pourrait implémenter la fonctionnalité coroutine:
la source
Une autre réponse mentionne que vous arrêtez les co-routines quand
"Fire1"
c'est terminé - c'est tout à fait correct, dans la mesure où la coroutine ne continue pas d'instancier GameObjects après la première pression de"Fire1"
.Dans votre cas, cependant, ce code ne restera pas «coincé» dans une boucle infinie, ce à quoi il semble que vous cherchiez une réponse - c'est-à-dire la
while(true) {}
boucle, même si vous ne l'avez pas arrêtée en externe.Il ne restera pas bloqué mais votre coroutine ne se terminera pas (sans appeler
StopCoroutine()
ouStopAllCoroutines()
) non plus. En effet, les coroutines Unity cèdent le contrôle à leur appelant.yield
ing est différent dereturning
:return
instruction arrêtera l'exécution d'une fonction, même s'il y a plus de code qui la suityield
instruction mettra la fonction en pause, en commençant à la ligne suivante aprèsyield
sa reprise.Habituellement, les coroutines seront reprises à chaque image, mais vous retournez également un
WaitForSeconds
objet.La ligne
yield return new WaitForSeconds(fireTime)
se traduit grosso modo par "maintenant, suspendez-moi et ne revenez pas avant que lesfireTime
secondes ne soient passées".À moins qu'elle ne soit arrêtée, il s'agit d'une coroutine qui, une fois démarrée, effectuera la boucle entière toutes les
fireTime
secondes.la source
Une explication simple: sous le capot, Unity est en train d'itérer sur une collection (de YieldInstruction s ou null ou tout ce que vous
yield return
) en utilisant leIEnumerator
que votre fonction retourne.Puisque vous utilisez le
yield
mot - clé, votre méthode est un itérateur . Ce n'est pas la chose Unity, c'est une fonctionnalité du langage C #. Comment ça marche?Il est paresseux et ne génère pas toute la collection à la fois (et la collection peut être infinie et impossible à générer en même temps). Les éléments de la collection sont générés selon les besoins. Votre fonction renvoie un itérateur avec lequel Unity peut travailler. Il appelle sa
MoveNext
méthode pour générer un nouvel élément et une nouvelleCurrent
propriété pour y accéder.Votre boucle n'est donc pas infinie, elle exécute du code, renvoie un élément et renvoie le contrôle à Unity afin qu'elle ne soit pas bloquée et puisse effectuer d'autres tâches telles que la gestion de votre entrée pour arrêter la coroutine.
la source
Pensez au fonctionnement d'un
foreach
:Le contrôle de l'itération est sur l'appelant - si vous arrêtez l'itération (ici avec
break
), c'est tout.Le
yield
mot-clé est un moyen simple de rendre un énumérable en C #. Le nom l'indique -yield return
redonne le contrôle à l'appelant (dans ce cas, le nôtreforeach
); c'est l'appelant qui décide quand passer à l'élément suivant. Vous pouvez donc créer une méthode comme celle-ci:Cela a l'air naïf de fonctionner pour toujours; mais en réalité, cela dépend entièrement de l'appelant. Vous pouvez faire quelque chose comme ça:
Cela peut être un peu déroutant si vous n'êtes pas habitué à ce concept, mais j'espère qu'il est également évident que c'est une propriété très utile. C'était le moyen le plus simple de donner le contrôle à votre appelant, et lorsque l'appelant décide de faire un suivi, il peut simplement faire l'étape suivante (si Unity a été créé aujourd'hui, il utiliserait probablement à la
await
place deyield
; maisawait
n'existait pas en arrière puis).Tout ce dont vous avez besoin pour implémenter vos propres coroutines (il va sans dire que les coroutines les plus stupides les plus simples) sont les suivantes:
Pour ajouter une
WaitForSeconds
implémentation très simple , vous avez juste besoin de quelque chose comme ceci:Et le code correspondant dans notre boucle principale:
Ta-da - c'est tout ce dont un système coroutine a besoin. Et en cédant le contrôle à l'appelant, l'appelant peut décider de n'importe quel nombre de choses; ils peuvent avoir une table d'événements triée plutôt que d'itérer dans toutes les coroutines sur chaque trame; ils peuvent avoir des priorités ou des dépendances. Il permet une mise en œuvre très simple du multitâche coopératif. Et regardez à quel point c'est simple, grâce à
yield
:)la source