Optimisations de code pour le développement de jeux structures de base et mentalités

Pour de nombreux développeurs indépendants débutants, l’optimisation du code devient presque une seconde pensée. Elle est ignorée au profit de moteurs ou de cadres, ou peut être considérée comme une technique plus «avancée», hors de leur portée. Cependant, il existe des méthodes d'optimisation pouvant être utilisées de manière plus basique, permettant à votre code de fonctionner plus efficacement et sur plus de systèmes. Jetons un coup d'oeil à quelques optimisations de code de base pour vous aider à démarrer.

Optimiser pour les joueurs et la santé mentale

Il n'est pas rare que les développeurs indépendants imitent les méthodes d'optimisation des grandes entreprises. Ce n’est pas forcément une mauvaise chose, mais s’efforcer d’optimiser votre jeu au-delà des retours utiles est un bon moyen de vous rendre fou. Une tactique intelligente pour surveiller l'efficacité de vos optimisations consiste à segmenter votre groupe démographique cible et à examiner les types de spécifications de leurs machines. Comparer votre jeu aux ordinateurs et aux consoles utilisés par vos joueurs potentiels vous aidera à garder un équilibre entre optimisation et santé mentale..

Optimisations de code de base

Malgré tout, il existe de nombreuses optimisations qui peuvent presque toujours être utilisées pour améliorer les performances de votre jeu. La plupart d'entre eux sont agnostiques (et certains moteurs et frameworks les prennent déjà en compte), je vais donc inclure des exemples de pseudocodes pour vous permettre de démarrer du bon pied. Nous allons jeter un coup d'oeil.

Minimiser l'impact des objets hors écran

Souvent gérées par les moteurs, et parfois même par les processeurs graphiques eux-mêmes, il est extrêmement important de minimiser la puissance de traitement des objets hors écran. Pour vos propres versions, il est judicieux de commencer à séparer vos objets en deux «couches», la première étant sa représentation graphique et la seconde ses données et ses fonctions (telles que son emplacement). Lorsqu'un objet est hors écran, nous n'avons plus besoin de dépenser les ressources pour le rendre, nous devons plutôt opter pour le suivi. Le suivi d'éléments, tels que l'emplacement et l'état, à l'aide de variables, réduit les ressources nécessaires à une fraction du coût initial..

Pour les jeux avec un grand nombre d’objets ou les objets contenant beaucoup de données, il peut même être utile d’aller plus loin en créant des routines de mise à jour distinctes, en définissant l’une pour mettre à jour lorsque l’objet est à l’écran et l’autre pour le mode hors écran . Définir une séparation plus avancée de cette manière peut éviter au système d'exécuter un certain nombre d'animations, d'algorithmes et d'autres mises à jour pouvant s'avérer inutiles lorsque l'objet est masqué..

Voici un exemple de pseudocode d’une classe d’objets utilisant des indicateurs et des contraintes d’emplacement:

Objet NPC Int locationX, locationY; // emplacement actuel de l'objet sur un plan 2D Fonction drawObject () // une fonction pour dessiner votre objet, à appeler dans votre boucle de mise à jour de l'écran // fonction qui vérifie si l'objet se trouve dans le port de la vue actuelle Fonction pollObjectDraw (tableau currentViewport [minX, minY, maxX, maxY]) // s'il se trouve dans la fenêtre d'affichage, indique qu'il peut être dessiné If (this.within (currentViewport)) Renvoie true;  Else Retour false;  

Bien que cet exemple soit simplifié, il nous permet de savoir si l'objet apparaîtra avant même de le dessiner, ce qui nous permet d'exécuter une fonction relativement simplifiée au lieu d'un appel de dessin complet. Pour séparer les fonctions autres que les appels graphiques, il peut être nécessaire d’utiliser un tampon supplémentaire - par exemple, une fonction qui inclurait tout ce qu’un joueur pourrait voir sous peu, plutôt que ce qu’il est en train de voir..

Séparer des mises à jour du cadre

En fonction du moteur ou de la structure que vous utilisez, il se peut que des objets soient mis à jour à chaque image ou cochés. Cela peut taxer un processeur assez rapidement, alors pour alléger ce fardeau, nous voudrons réduire les appels à chaque image autant que possible..

La première chose que nous voulons séparer est le rendu de fonction. Ces appels consomment généralement beaucoup de ressources. Par conséquent, l'intégration d'un appel qui peut nous avertir lorsque les propriétés visuelles d'un objet ont changé peut réduire considérablement le rendu..

Pour aller plus loin, nous pouvons utiliser un écran temporaire pour nos objets. En faisant en sorte que les objets soient directement attirés vers ce conteneur temporaire, nous pouvons nous assurer qu'ils ne sont dessinés que lorsque c'est nécessaire.

Semblable à la première optimisation mentionnée ci-dessus, l'itération de départ de notre code introduit une interrogation simple:

Objet NPC boolean hasChanged; // attribue la valeur true à cet indicateur chaque fois qu'une modification est apportée à l'objet // fonction qui indique si la fonction pollObjectChanged (return hasChanged (); 

Pendant chaque image maintenant, plutôt que d’exécuter un certain nombre de fonctions, nous pouvons voir si cela est même nécessaire. Bien que cette implémentation soit aussi simple, elle peut déjà commencer à montrer des gains énormes en efficacité de votre jeu, en particulier en ce qui concerne les objets statiques et les objets à mise à jour lente comme un HUD..

Pour aller plus loin dans votre propre jeu, diviser le drapeau en plusieurs composants plus petits peut être utile pour segmenter les fonctionnalités. Par exemple, vous pouvez avoir des indicateurs pour un changement de données et un changement graphique se produire séparément.

Calculs en direct et valeurs de recherche

Cette optimisation est utilisée depuis les débuts des systèmes de jeu. Déterminer les compromis entre les calculs en temps réel et les recherches de valeur peut aider à réduire considérablement les temps de traitement. Une utilisation bien connue dans l’histoire du jeu consiste à stocker les valeurs des fonctions de trigonométrie dans des tables car, dans la plupart des cas, il était plus efficace de stocker une grande table et de la récupérer plutôt que de faire les calculs à la volée et d’exercer une pression supplémentaire. sur le CPU.

En informatique moderne, nous avons rarement besoin de choisir entre le stockage des résultats et l'exécution d'un algorithme. Cependant, il existe encore des situations dans lesquelles cela peut réduire les ressources utilisées, permettant l'inclusion d'autres fonctionnalités sans surcharger le système..

Un moyen facile de commencer à implémenter cela consiste à identifier les calculs courants, ou des morceaux de calcul, au sein de votre jeu: plus le calcul est volumineux, mieux c'est. Exécuter des bits d'algorithmes récurrents une seule fois et les stocker peut souvent économiser une quantité considérable de puissance de traitement. Même isoler ces pièces dans des boucles de jeu spécifiques peut aider à optimiser les performances.

Par exemple, dans de nombreux tireurs de haut en bas, il y a souvent de grands groupes d'ennemis ayant les mêmes comportements. S'il y a 20 ennemis, chacun se déplaçant le long d'un arc, plutôt que de calculer chaque mouvement individuellement, il est plus efficace de stocker les résultats de l'algorithme. Cela permet de le modifier en fonction de la position de départ de chaque ennemi..

Pour déterminer si cette méthode est utile pour votre jeu, essayez d’utiliser le benchmarking pour comparer la différence de ressources utilisée entre le calcul en temps réel et le stockage de données..

Utilisation de l'inactivité du processeur

Bien que cela joue davantage sur l'utilisation des ressources en veille, avec une réflexion approfondie sur vos objets et vos algorithmes, nous pouvons empiler les tâches de manière à renforcer l'efficacité de notre code..

Pour commencer à utiliser la sensibilité à l'oisiveté dans votre propre logiciel, vous devez d'abord séparer les tâches de votre jeu qui ne sont pas critiques dans le temps ou qui peuvent être calculées avant d'être utilisées. Le premier domaine à rechercher du code qui entre dans cette catégorie est la fonctionnalité qui est strictement liée à l'atmosphère du jeu. Les systèmes météorologiques qui n'interagissent pas avec la géographie, les effets visuels d'arrière-plan et l'audio d'arrière-plan peuvent facilement s'intégrer au calcul en veille.

Au-delà des éléments strictement atmosphériques, les calculs garantis constituent un autre type de calcul pouvant être placé dans des espaces inactifs. Les calculs d’intelligence artificielle qui se produiront indépendamment de l’interaction du joueur (qu’ils ne tiennent pas compte du joueur ou qu’ils n’exigent probablement pas encore l’interaction du joueur) peuvent être rendus plus efficaces, tout comme les mouvements calculés, tels que événements scriptés.

Créer un système qui utilise l'oisiveté ne se limite pas à une plus grande efficacité: il peut également être utilisé pour redimensionner les yeux dans les yeux. Par exemple, sur une plate-forme bas de gamme, un joueur peut simplement expérimenter une version vanille du gameplay. Toutefois, si notre système détecte des images inactives, nous pouvons l’utiliser pour ajouter des particules, des événements graphiques et d’autres modifications atmosphériques supplémentaires afin de donner un peu plus de panache au jeu..

Pour implémenter cela, utilisez les fonctionnalités disponibles dans votre moteur, votre infrastructure ou votre langage favori pour évaluer la quantité de ressources processeur utilisée. Définissez des indicateurs dans votre code qui permettent de vérifier facilement la quantité de puissance de traitement «supplémentaire» disponible, puis configurez vos sous-systèmes pour qu'ils examinent cet indicateur et se comportent en conséquence..

Chaînage des optimisations ensemble

En combinant ces méthodes, il est possible d'améliorer considérablement l'efficacité de votre code. Cette efficacité s'accompagne de la possibilité d'ajouter plus de fonctionnalités, de fonctionner sur plus de systèmes et d'assurer une expérience plus solide aux joueurs..

Avez-vous facilement mis en œuvre des optimisations de code que vous utilisez régulièrement? Laissez-moi savoir à leur sujet!