J'ai un algorithme de génération de niveau qui est lourd en calcul. En tant que tel, l'appel entraîne toujours le blocage de l'écran du jeu. Comment puis-je placer la fonction sur un deuxième thread alors que le jeu continue à afficher un écran de chargement pour indiquer que le jeu n'est pas gelé?
unity
multithreading
DarkDestry
la source
la source
List
d’Actions pour stocker les fonctions que vous souhaitez appeler dans le fil principal. verrouiller et copier laAction
liste dans laUpdate
fonction à la liste temporaire, effacer la liste d'origine, puis exécuter leAction
code dans celuiList
du thread principal. Voir UnityThread de mon autre article pour savoir comment faire cela. Par exemple, pour appeler une fonction sur le fil principal,UnityThread.executeInUpdate(() => { transform.Rotate(new Vector3(0f, 90f, 0f)); });
Réponses:
Mise à jour: en 2018, Unity déploie un système de travail C # comme moyen de décharger du travail et d'utiliser plusieurs cœurs de processeur.
La réponse ci-dessous est antérieure à ce système. Cela fonctionnera toujours, mais il existe peut-être de meilleures options dans Unity moderne, en fonction de vos besoins. En particulier, le système de travail semble résoudre certaines des limitations auxquelles les threads créés manuellement peuvent accéder en toute sécurité, décrites ci-dessous. Les développeurs expérimentant avec le rapport de prévisualisation signalent par exemple des radios et des constructions de maillages en parallèle .
J'inviterais les utilisateurs expérimentés dans ce système d'emploi à ajouter leurs propres réponses reflétant l'état actuel du moteur.
Auparavant, j'avais déjà utilisé le threading pour les tâches lourdes dans Unity (généralement le traitement des images et de la géométrie). Ce n'est pas très différent de l'utilisation de threads dans d'autres applications C #, avec deux mises en garde:
Comme Unity utilise un sous-ensemble un peu plus ancien de .NET, il existe certaines fonctionnalités et bibliothèques de threads plus récentes que nous ne pouvons pas utiliser telles quelles, mais les bases sont toujours là.
Comme Almo le note dans un commentaire ci-dessus, de nombreux types d'Unity ne sont pas threadsafe et jetteront des exceptions si vous essayez de les construire, de les utiliser ou même de les comparer à partir du thread principal. Points à garder à l'esprit:
Un cas courant consiste à vérifier si une référence GameObject ou Monobehaviour est nulle avant de tenter d'accéder à ses membres.
myUnityObject == null
appelle un opérateur surchargé pour tout ce qui est issu de UnityEngine.Object, maisSystem.Object.ReferenceEquals()
contourne-le dans une certaine mesure - rappelez-vous simplement qu'un GameObject Destroy () ed compare égal à null en utilisant la surcharge, mais n'est pas encore à null.La lecture de paramètres à partir de types Unity est généralement sûre sur un autre thread (en ce sens qu'elle ne lève pas immédiatement une exception tant que vous veillez à vérifier les null comme ci-dessus), mais notez ici l'avertissement de Philipp selon lequel le thread principal pourrait être en train de modifier son état. pendant que tu le lis. Vous devrez faire preuve de discipline en ce qui concerne les personnes autorisées à modifier quoi et quand afin d'éviter la lecture de certains états incohérents, ce qui peut entraîner des bogues difficiles à localiser car ils dépendent de la minuterie inférieure à la milliseconde entre les threads. ne pas reproduire à volonté.
Les membres statiques Random et Time ne sont pas disponibles. Créez une instance de System.Random par thread si vous avez besoin d'aléatoire et System.Diagnostics.Stopwatch si vous avez besoin d'informations temporelles.
Les fonctions Mathf, Vector, Matrix, Quaternion et Color fonctionnent parfaitement sur tous les threads. Vous pouvez ainsi effectuer la plupart de vos calculs séparément.
La création de GameObjects, l'attachement de Monobehaviours, ou la création / mise à jour de textures, de maillages, de matériaux, etc. doivent tous se produire sur le fil principal. Dans le passé, lorsque j'avais besoin de travailler avec ceux-ci, j'avais configuré une file d'attente producteur-consommateur, où mon thread de travail préparait les données brutes (comme un grand tableau de vecteurs / couleurs à appliquer à un maillage ou une texture), et une mise à jour ou une coroutine sur le thread principal interroge les données et les applique.
Avec ces notes en retrait, voici un motif que j'utilise souvent pour le travail en mode fileté. Je ne garantis pas qu'il s'agit d'un style de pratique exemplaire, mais le travail est fait. (Les commentaires ou modifications à améliorer sont les bienvenus - je sais que le filetage est un sujet très profond dont je ne connais que les bases)
Si vous n'avez pas strictement besoin de répartir le travail sur plusieurs threads pour gagner du temps, et que vous cherchez simplement un moyen de le rendre non bloquant afin que le reste de votre jeu continue de tourner, une solution plus légère dans Unity est Coroutines . Ce sont des fonctions qui peuvent faire un peu de travail, puis céder le contrôle au moteur pour continuer ce qu’il fait, et le reprendre de manière transparente plus tard.
Cela ne nécessite aucune considération de nettoyage particulière, car le moteur (pour autant que je sache) supprime les coroutines d'objets détruits pour vous.
Tout l’état local de la méthode est conservé lorsqu’il cède et reprend, de sorte que, dans de nombreux cas, il est comme s’il fonctionnait sans interruption sur un autre thread (mais vous avez toutes les commodités de l’exécution sur le thread principal). Vous devez simplement vous assurer que chaque itération est suffisamment courte pour ne pas ralentir votre thread principal de manière déraisonnable.
En vous assurant que les opérations importantes ne sont pas séparées par un rendement, vous pouvez obtenir la cohérence d'un comportement à un seul thread - en sachant qu'aucun autre script ou système du thread principal ne peut modifier les données sur lesquelles vous êtes en train de travailler.
La ligne de retour de rendement vous donne quelques options. Vous pouvez...
yield return null
pour reprendre après la mise à jour de la prochaine image ()yield return new WaitForFixedUpdate()
à reprendre après la prochaine FixedUpdate ()yield return new WaitForSeconds(delay)
à reprendre après un certain temps de jeuyield return new WaitForEndOfFrame()
à reprendre une fois le rendu de l'interface graphique terminéyield return myRequest
oùmyRequest
est une instance WWW , à reprendre une fois que le chargement des données demandées est terminé à partir du Web ou du disque.yield return otherCoroutine
oùotherCoroutine
est une instance Coroutine , à reprendre une foisotherCoroutine
terminée. Ceci est souvent utilisé dans la formeyield return StartCoroutine(OtherCoroutineMethod())
pour enchaîner l'exécution à une nouvelle coroutine qui peut elle-même céder quand elle le souhaite.Expérimentalement, ignorer le second
StartCoroutine
et simplement écrire permet d'yield return OtherCoroutineMethod()
atteindre le même objectif si vous souhaitez enchaîner l'exécution dans le même contexte.L’emballage dans un objet
StartCoroutine
peut toujours être utile si vous souhaitez exécuter la coroutine imbriquée en association avec un deuxième objet, tel queyield return otherObject.StartCoroutine(OtherObjectsCoroutineMethod())
... selon le moment où vous voulez que la coroutine prenne son prochain tour.
Ou
yield break;
bien arrêter la coroutine avant la fin, comme vous le feriez pour mettre au pointreturn;
une méthode conventionnelle.la source
Vous pouvez mettre votre calcul lourd dans un autre thread, mais l'API de Unity n'est pas thread-safe, vous devez les exécuter dans le thread principal.
Eh bien, vous pouvez essayer ce package sur Asset Store pour vous aider à utiliser les threads plus facilement. http://u3d.as/wQg Vous pouvez simplement utiliser une seule ligne de code pour démarrer un thread et exécuter l'API Unity en toute sécurité.
la source
En utilisant Svelto.Tasks, vous pouvez très facilement renvoyer le résultat d’une routine multithread au thread principal (et donc aux fonctions d’unité):
http://www.sebaslab.com/svelto-taskrunner-run-serial-and-parallel-asasynchronous-tasks-in-unity3d/
la source
@DMGregory l'a très bien expliqué.
Le filetage et les coroutines peuvent être utilisés. Ancien pour décharger le thread principal et ensuite pour rendre le contrôle au thread principal. Pousser des charges lourdes sur du fil séparé semble plus raisonnable. Par conséquent, les files d’attente peuvent être ce que vous recherchez.
Il existe un très bon exemple de script JobQueue sur Unity Wiki.
la source