Il s'agit du cinquième d'une série d'articles qui présente des conseils d'optimisation pour vos projets Unity. Utilisez-les comme guide pour fonctionner à des fréquences d'images plus élevées avec moins de ressources. Une fois que vous avez essayé ces bonnes pratiques, assurez-vous de consulter les autres pages de la série :
- Configurer votre projet Unity pour de meilleures performances
- Optimisation des performances pour les graphiques haut de gamme
- Gestion de l'utilisation du GPU pour les jeux PC et console
- Programmation avancée et architecture de code
La physique peut créer un gameplay complexe, mais cela a un coût en termes de performances. Une fois que vous connaissez ces coûts, vous pouvez modifier la simulation pour les gérer de manière appropriée. Utilisez ces conseils pour rester dans votre fréquence d'images cible et créer une lecture fluide grâce à la physique intégrée de Unity (NVIDIA PhysX).
Les maillages utilisés en physique passent par un processus appelé Cooking. Cela prépare le maillage afin qu'il puisse fonctionner avec des requêtes physiques telles que des raycasts, des contacts, etc.
Un MeshCollider dispose de plusieurs CookingOptions pour vous aider à valider le maillage pour la physique. Si vous êtes certain que votre maillage n'a pas besoin de ces vérifications, vous pouvez les désactiver pour accélérer votre temps de cuisson.
Dans les CookingOptions de chaque MeshCollider, décochez EnableMeshCleaning, WeldColocatedVerticeset CookForFasterSimulation. Ces options sont utiles pour les maillages générés de manière procédurale au moment de l'exécution, mais peuvent être désactivées si vos maillages possèdent déjà les triangles appropriés.
Si vous ciblez spécifiquement le PC, assurez-vous de garder Use Fast Midphase activé. Cela passe à un algorithme plus rapide de PhysX 4.1 pendant la phase intermédiaire de la simulation (ce qui permet de réduire un petit ensemble de triangles potentiellement sécants pour les requêtes physiques). Les plates-formes autres que les ordinateurs de bureau doivent toujours utiliser l'algorithme plus lent qui génère les R-trees.
Les collisionneurs de maillage sont généralement chers, pensez donc à remplacer les collisionneurs de maillage plus complexes par des collisionneurs primitifs ou simplifiés pour se rapprocher de la forme originale.
Apprenez-en plus dans la documentation CookingOptions.
Si vous générez des maillages de manière procédurale pendant le jeu, vous pouvez créer un Mesh Collider au moment de l'exécution. Cependant, l'ajout d'un composant Mesh Collider directement au maillage cuit la physique sur le thread principal. Cela peut consommer beaucoup de temps CPU.
Utilisez Physics.BakeMesh pour préparer un maillage à utiliser avec un Mesh Collider et enregistrez les données ancrées avec le maillage lui-même. Un nouveau Mesh Collider référençant ce maillage réutilisera ces données pré-préparées, plutôt que de cuire à nouveau le maillage. Cela peut aider à réduire le temps de chargement de la scène ou le temps d’instanciation ultérieur. Pour optimiser les performances, vous pouvez décharger la cuisson du maillage vers un autre thread avec le système de tâches C#.
Reportez-vous à cet exemple pour plus de détails sur la façon de créer des maillages sur plusieurs threads.
Dans les paramètresdu lecteur , vérifiez les maillages de collision pré-cuits autant que possible. Nous vous recommandons également de revoir la configuration de la matrice de collision pour vous assurer que les objets du joueur et de la mécanique de jeu se trouvent dans les couches appropriées.
Supprimer les rappels des déclencheurs pour les couches inutiles peut être une grande victoire, alors essayez de simplifier votre Layer Collision Matrix. Vous pouvez modifier vos paramètres physiques via Paramètres du projet > Physique.
Apprenez-en davantage dans la documentation de Collision Matrix.
Les moteurs physiques fonctionnent en s'exécutant sur un pas de temps fixe. Pour voir le taux fixe auquel votre projet s'exécute, accédez à Édition > Paramètres du projet > Heure.
Le champ Pas de temps fixe définit le delta de temps utilisé par chaque étape physique. Par exemple, la valeur par défaut de 0,02 seconde (20 ms) équivaut à 50 images par seconde (fps) ou 50 Hz.
Étant donné que chaque image dans Unity prend un temps variable, elle n'est pas parfaitement synchronisée avec la simulation physique. Le moteur compte jusqu'au prochain pas de temps physique. Si une image s'exécute légèrement plus lentement ou plus rapidement, Unity utilise le temps écoulé pour savoir quand exécuter la simulation physique au pas de temps approprié.
Dans le cas où la préparation d’une trame prend beaucoup de temps, cela peut entraîner des problèmes de performances. Ainsi, si votre jeu connaît un pic (par exemple, instanciation de nombreux GameObjects ou chargement d'un fichier à partir du disque), l'exécution de l'image peut prendre 40 ms ou plus. Avec le pas de temps fixe par défaut de 20 ms, cela entraînerait l'exécution de deux simulations physiques sur l'image suivante pour « rattraper » le pas de temps variable.
Des simulations physiques supplémentaires, à leur tour, ajoutent plus de temps pour traiter la trame. Sur les plates-formes bas de gamme, cela conduit potentiellement à une spirale descendante des performances.
Une trame ultérieure prenant plus de temps à préparer allonge également le retard dans les simulations physiques. Cela conduit à des images encore plus lentes et à davantage de simulations à exécuter par image. Le résultat est une performance affaiblie.
À terme, le temps entre les mises à jour physiques pourrait dépasser le pas de temps maximum autorisé. Après cette coupure, Unity commence à supprimer les mises à jour physiques et le jeu bégaie.
Pour éviter les problèmes de performances avec la physique :
- Réduisez la fréquence de simulation: Pour les plates-formes bas de gamme, augmentez le pas de temps fixe légèrement supérieur à votre fréquence d'images cible. Par exemple, utilisez 0,035 seconde pour 30 ips sur mobile. Cela pourrait aider à éviter cette spirale descendante des performances.
- Diminuez le pas de temps maximum autorisé : L'utilisation d'une valeur plus petite (comme 0,1 seconde) sacrifiera une certaine précision de la simulation physique, mais limitera également le nombre de mises à jour physiques pouvant avoir lieu dans une seule image. Expérimentez avec des valeurs pour trouver quelque chose qui répond aux exigences de votre projet.
- Simulez l'étape physique manuellement si nécessaire : Désactivez l'option Simulation automatique dans les paramètres physiques et invoquez directement Physics.Simulate pendant la phase de mise à jour du cadre. Cela vous permet de déterminer activement quand exécuter l’étape physique. Passez Time.deltaTime à Physics.Simulateafin de garder la physique synchronisée avec le temps de simulation.
- remarque Cette approche peut provoquer des instabilités dans la simulation physique, en particulier dans les scènes présentant une physique complexe ou des temps d'image très variables. Utilisez-le avec prudence.
Apprenez-en davantage dans la documentation Physics.Simulate.
Le moteur physique Unity fonctionne en deux étapes :
- La phase large: Collecte les collisions potentielles à l'aide d'un algorithme Sweep and Prune
- La phase étroite: Quand le moteur calcule réellement les collisions
Le paramètre par défaut de phase large de Sweep and Prune Broadphase (Edit > Project Settings > Physics > Broadphase Type) peut générer des faux positifs pour des mondes généralement plats et comportant de nombreux collisionneurs. Si votre scène est grande et essentiellement plate, évitez ce problème et passez à l'élagage automatique des boîtes ou à l'élagage multibox Broadphase. Ces options divisent le monde en une grille, où chaque cellule de la grille effectue un balayage et un élagage.
Multibox Pruning Broadphase vous permet de spécifier manuellement les limites du monde et le nombre de cellules de la grille, tandis que l'Automatic Box Pruning calcule tout cela pour vous.
Consultez la liste complète des propriétés physiques ici.
Si vous souhaitez simuler plus précisément un corps physique spécifique, augmentez son Rigidbody.solverIterations. Cela remplace Physics.defaultSolverIterations, qui peut être trouvé via Edition > Paramètres du projet > Physique > Itérations du solveur par défaut.
Pour optimiser vos simulations physiques, définissez une valeur relativement faible dans le paramètre defaultSolveIterationsdu projet. Appliquez ensuite des valeurs Rigidbody.solverIterations personnalisées plus élevées aux instances individuelles qui nécessitent plus de détails.
Obtenez plus d’informations sur Rigidbody.solverIterations.
Lorsque vous mettez à jour une transformation, Unity ne la synchronise pas automatiquement avec le moteur physique. Unity accumule les transformations et attend soit que la mise à jour physique soit exécutée, soit que l'utilisateur appelle Physics.SyncTransforms.
Si vous souhaitez synchroniser la physique avec vos transformations plus fréquemment, vous pouvez définir Physics.autoSyncTransform sur True (également disponible dans Project Settings > Physics > Auto Sync Transforms). Lorsque cette option est activée, tout Rigidbody ou Collider sur ce Transform, ainsi que ses enfants, sont automatiquement mis à jour avec le Transform.
Toutefois, si cela n’est pas absolument nécessaire, désactivez-le. Une série de requêtes physiques successives (telles que des raycasts) peuvent entraîner une perte de performances.
En savoir plus sur Physics.SyncTransforms.
Les événements de collision et de déclenchement (OnCollisionEnter, OnCollisionStay, OnCollisionExit, OnTriggerEnter, OnTriggerStay, OnTriggerExit) se déclencheront pour tout MonoBehaviour qui implémente ces fonctions et remplit les critères d'interaction. Ces événements seront également envoyés aux MonoBehaviors désactivés.
C'est pourquoi il est recommandé de n'implémenter ces fonctions qu'en cas de besoin, car des fonctions vides et non essentielles seront appelées. Des précautions particulières doivent être prises avec OnCollisionStay et OnTriggerStay car ceux-ci peuvent être appelés plusieurs fois en fonction du nombre de collisionneurs impliqués.
Les rappelsMonoBehaviour.OnCollisionEnter, MonoBehaviour.OnCollisionStayet MonoBehaviour.OnCollisionExit prennent également une instance de collision comme paramètre. Cette instance de collision est allouée sur le tas géré et doit être récupérée.
Pour réduire la quantité de déchets générés, activez Physics.reuseCollisionCallbacks(trouvé dans Project Settings > Physics > Reuse Collision Callbacks). Avec cette option active, Unity n'attribue qu'une seule instance de paire de collision à chaque rappel. Cela réduit les déchets pour le garbage collector (GC) et améliore les performances globales.
remarque Si vous référencez l’instance de collision en dehors des rappels de collision pour le post-traitement, vous devez désactiver Réutiliser les rappels de collision.
En savoir plus sur Physics.reuseCollisionCallbacks.
Les Static Colliders sont des GameObjects avec un composant Collider mais sans Rigidbody. Contrairement à son nom, vous pouvez déplacer un collisionneur statique.
Modifiez simplement la position du corps physique, accumulez les changements de position et synchronisez avant la mise à jour physique. Vous n'avez pas besoin d'ajouter un composant Rigidbody au Static Collider juste pour le déplacer. Mais si vous souhaitez que le Static Collider interagisse avec d'autres corps physiques de manière plus complexe, donnez-lui un Rigidbody cinématique. Utilisez Rigidbody.position et Rigidbody.rotation pour le déplacer au lieu d'accéder au composant Transform. Cela garantit un comportement plus prévisible du moteur physique.
remarque En physique 2D, ne déplacez pas les collisionneurs statiques car la reconstruction de l'arborescence prend du temps.
Obtenez plus d’informations sur les corps rigides.
Pour détecter et collecter des collisionneurs à une certaine distance, dans une direction particulière, utilisez des raycasts et d'autres requêtes physiques comme BoxCast.
Les requêtes physiques qui renvoient plusieurs collisionneurs sous forme de tableau, comme OverlapSphere ou OverlapBox, doivent allouer ces objets sur le tas géré. Cela signifie que le garbage collector doit finalement collecter les objets alloués, ce qui peut diminuer les performances si cela se produit au mauvais moment.
Pour réduire cette surcharge, utilisez les versions NonAlloc de ces requêtes. Par exemple, si vous utilisez OverlapSphere pour collecter tous les collisionneurs potentiels autour d'un point, veillez à utiliser OverlapSphereNonAlloc. Cela vous permet de transmettre un tableau de collisionneurs (le paramètre de résultats) pour agir comme un tampon.
La méthode NonAlloc fonctionne sans générer de déchets. Sinon, elle fonctionne comme la méthode d’allocation correspondante. N'oubliez pas de définir un tampon de résultats de taille suffisante lorsque vous utilisez une méthode NonAlloc. Le tampon n'augmente pas s'il manque d'espace.
Apprenez-en davantage dans la documentation de la méthode NonAlloc.
Bien que vous puissiez exécuter des requêtes raycast avec Physics.Raycast, cela peut prendre beaucoup de temps CPU. Cela est particulièrement vrai si vous effectuez un grand nombre d'opérations raycast (par exemple, calcul de la ligne de vue pour 10 000 agents).
Utilisez RaycastCommand pour regrouper la requête à l'aide du système de tâches C#. Cela décharge le travail du thread principal afin que les raycasts puissent se produire de manière asynchrone et en parallèle.
Voir un exemple dans la documentation RaycastCommands.
Utilisez la fenêtre Physics Debug (Fenêtre > Analyse > Physics Debugger) pour vous aider à résoudre tout problème de collision ou de divergence. Cette fenêtre affiche un indicateur à code couleur des GameObjects qui peuvent entrer en collision les uns avec les autres.
Pour plus d’informations, consultez la page de visualisation Physics Debug.
L'un de nos guides les plus complets rassemble plus de 80 conseils pratiques sur la façon d'optimiser vos jeux pour PC et console. Créés par nos ingénieurs experts en Accelerate Solutions , ces conseils approfondis vous aideront à tirer le meilleur parti de Unity et à améliorer les performances de votre jeu.