Que recherchez-vous ?
Hero background image
Last updated January 2020, 10 min. read

Trois techniques pour concevoir votre jeu avec des objets programmables

Cette page a été traduite automatiquement pour faciliter votre expérience. Nous ne pouvons pas garantir l'exactitude ou la fiabilité du contenu traduit. Si vous avez des doutes quant à la qualité de cette traduction, reportez-vous à la version anglaise de la page web.

Ce que vous obtiendrez de cette page: Conseils pour garder votre code de jeu facile à modifier et à déboguer en l'architecturant avec des objets scriptables.

Ces conseils proviennent de Ryan Hipple, ingénieur principal chez Schell Games, qui a une expérience avancée de l'utilisation de objets scriptables pour architecturer des jeux. Vous pouvez regarder la présentation de Ryan à Unite sur les objets scriptables ici; nous vous recommandons également de voir la session de l'ingénieur Unity Richard Fine pour une excellente introduction aux objets scriptables.

Que sont les objets programmables ?

L'objet scriptable est une classe Unity sérialisable qui vous permet de stocker de grandes quantités de données partagées indépendamment des instances de script. Leur utilisation facilite la gestion des modifications et le débogage. Vous pouvez établir un niveau de communication flexible entre les différents systèmes de votre jeu, afin qu'il soit plus facile de les modifier et de les adapter tout au long de la production, ainsi que de réutiliser des composants.

Les trois piliers de la conception d'un jeu

Utilisez un design modulaire:

  • Évitez de créer des systèmes qui dépendent directement les uns des autres. Par exemple, un système d'inventaire devrait pouvoir communiquer avec d'autres systèmes de votre jeu, mais vous ne voulez pas créer de référence directe entre eux, car cela rend difficile le réassemblage des systèmes dans différentes configurations et relations.
  • Créez des scènes comme des ardoises vierges : évitez d'avoir des données transitoires existant entre vos scènes. Chaque fois que vous commencez une scène, vous devez faire table rase. Cela vous permet d'avoir des scènes qui ont un comportement unique qui n'était pas présent dans d'autres scènes, sans avoir à faire de hack.
  • Configurez les Prefabs pour qu'ils fonctionnent de manière autonome. Dans la mesure du possible, chaque prefab que vous intégrez à une scène doit contenir sa fonctionnalité. Cela vous sera d'une grande aide dans le contrôle de version avec des équipes plus importantes, où les scènes sont une liste de prefabs et où ces derniers contiennent leur fonctionnalité individuelle. De cette façon, la plupart de vos validations se font au niveau du prefab, ce qui entraîne moins de conflits dans la scène.
  • Concentrez chaque composant sur la résolution d'un seul problème. Cela facilite l'assemblage de plusieurs composants pour créer quelque chose de nouveau.

Rendez-le facile à changer et à éditer des parties :

  • Rendez autant de votre jeu basé sur des données que possible. Lorsque vous concevez vos systèmes de jeu comme des machines qui traitent des données en tant qu'instructions, vous pouvez apporter des modifications au jeu de manière efficace, même lorsqu'il est en cours d'exécution.
  • Si vos systèmes sont configurés pour être aussi modulaires et basés sur des composants que possible, il est plus facile de les éditer, y compris pour vos artistes et designers. Si les designers peuvent assembler des éléments dans le jeu sans avoir à demander une fonctionnalité explicite – en grande partie grâce à la mise en œuvre de petits composants qui ne font qu'une seule chose – ils peuvent potentiellement combiner ces composants de différentes manières pour trouver de nouvelles mécaniques de jeu, Ryan dit que certaines des fonctionnalités les plus intéressantes sur lesquelles son équipe a travaillé dans leurs jeux proviennent de ce processus, ce qu'il appelle "design émergent".
  • Il est crucial que votre équipe puisse apporter des modifications au jeu à l'exécution. Plus vous pouvez changer votre jeu à l'exécution, plus vous pouvez trouver un équilibre et des valeurs, et, si vous êtes capable de sauvegarder votre état d'exécution comme le font les objets scriptables, vous êtes dans une excellente position.

Rendez-le facile à déboguer :

Celui-ci est vraiment un sous-pilier des deux premiers. Plus votre jeu est modulaire, plus il est facile de tester chacun de ses éléments. Plus il est modifiable (plus il a de fonctionnalités disposant d'une vue dans l'Inspector), plus il est facile à déboguer. Assurez-vous de pouvoir voir l'état de débogage dans l'Inspecteur et ne considérez jamais une fonctionnalité comme complète tant que vous n'avez pas de plan sur la façon dont vous allez la déboguer.

Concevez des variables

L'une des choses les plus simples que vous pouvez construire avec les ScriptableObjects est une variable autonome basée sur un actif. Ci-dessous se trouve un exemple pour un FloatVariable mais cela s'étend à tout autre type sérialisable également.

Tout le monde dans votre équipe, peu importe son niveau technique, peut définir une nouvelle variable de jeu en créant un nouvel actif FloatVariable. Tout MonoBehaviour ou ScriptableObject peut utiliser un FloatVariable public plutôt qu'un float public afin de référencer cette nouvelle valeur partagée.

Encore mieux, si un MonoBehaviour change la valeur d'un FloatVariable, d'autres MonoBehaviours peuvent voir ce changement. Cela crée une couche de messagerie entre des systèmes qui n'ont pas besoin de références les uns aux autres.

Exemple: Points de vie du joueur

Exemple: Points de vie du joueur

Un exemple d'utilisation pour cela est les points de vie (PV) d'un joueur. Dans une partie solo en local, les PV du joueur peuvent être une ressource FloatVariable baptisée PlayerHP. Lorsque le joueur subit des dégâts, cela soustrait des PVJoueur et lorsque le joueur se soigne, cela ajoute aux PVJoueur.

Maintenant, imaginez un Prefab de barre de santé dans la scène. La jauge de vie contrôle la variable PlayerHP pour actualiser son affichage. Si le code reste inchangé, elle pourrait tout aussi bien désigner autre chose, comme une variable PlayerMP. La barre de santé ne sait rien sur le joueur dans la scène, elle lit simplement la même variable que le joueur écrit.

Une fois que nous sommes configurés de cette manière, il est facile d'ajouter d'autres éléments pour surveiller le PlayerHP. Le système musical peut changer lorsque le PlayerHP est bas, les ennemis peuvent changer leurs schémas d'attaque lorsqu'ils savent que le joueur est faible, les effets d'espace écran peuvent souligner le danger de la prochaine attaque, et ainsi de suite. L'important ici, est que le script Player n'envoie pas de messages à ces systèmes et que ces systèmes n'ont pas besoin de connaître le GameObject du joueur. Vous pouvez également aller dans l'Inspecteur lorsque le jeu est en cours d'exécution et changer la valeur de PlayerHP pour tester des choses.

Lors de l'édition de la valeur d'un FloatVariable, il peut être judicieux de copier vos données dans une valeur d'exécution pour ne pas changer la valeur stockée sur le disque pour le ScriptableObject. Si vous faites cela, les MonoBehaviours devraient accéder à RuntimeValue pour éviter de modifier l'InitialValue qui est enregistré sur le disque.

Concevez des événements

Une des fonctionnalités préférées de Ryan à construire sur les ScriptableObjects est un système d'événements. Les architectures d'événements aident à modulariser votre code en envoyant des messages entre les systèmes qui ne communiquent pas directement entre eux. Ils permettent aux éléments de réagir à un changement d'état sans le surveiller constamment dans une boucle de mise à jour.

Les exemples de code suivants proviennent d'un système d'événements qui se compose de deux parties : un GameEvent ScriptableObject et un GameEventListener MonoBehaviour. Les concepteurs peuvent créer le nombre de GameEvents de leur choix dans le projet pour représenter les messages importants qui peuvent être envoyés. Un GameEventListener attend qu'un GameEvent spécifique soit déclenché et répond en invoquant un UnityEvent (qui n'est pas un véritable événement, mais plutôt un appel de fonction sérialisé).

Exemple de code: Objet Scriptable GameEvent

GameEvent ScriptableObject :

Exemple de code: GameEventListener

GameEventListener :

Un système d'événements qui gère la mort du joueur

Un système d'événements qui gère la mort du joueur

Un exemple de cela est la gestion de la mort du joueur dans un jeu. Cela peut modifier l'exécution du tout au tout, mais déterminer l'endroit où programmer la logique peut s'avérer complexe. Le script Player doit-il déclencher l'interface Game Over ou un changement sonore ? Les ennemis doivent-ils s'obstiner à vérifier si le joueur est toujours en vie ? Un système d'événements nous permet d'éviter des dépendances problématiques comme celles-ci.

Lorsque le joueur meurt, le script Player appelle Raise sur l'événement OnPlayerDied. Le script Player n'a pas besoin de savoir quels systèmes sont concernés, puisqu'il s'agit d'une simple diffusion. L'IU Game Over suit l'événement OnPlayerDied et est activée, un script de caméra peut également suivre cet événement et afficher un écran noir, et la bande-son peut répondre avec un changement de musique. Nous pouvons également faire en sorte que chaque ennemi écoute OnPlayerDied, déclenchant une animation de provocation ou un changement d'état pour revenir à un comportement inactif.

Ce modèle rend incroyablement facile l'ajout de nouvelles réponses à la mort du joueur. De plus, il est facile de tester la réponse à la mort du joueur en appelant Raise sur l'événement depuis un code de test ou un bouton dans l'Inspecteur.

Le système d'événements qu'ils ont construit chez Schell Games est devenu quelque chose de beaucoup plus complexe et possède des fonctionnalités pour permettre le passage de données et la génération automatique de types. Cet exemple était essentiellement le point de départ de ce qu'ils utilisent aujourd'hui.

Concevez d'autres systèmes

Les objets Scriptables ne doivent pas être que des données. Prenez n'importe quel système implémenté dans un élément MonoBehaviour et voyez si vous pouvez déplacer l'implémentation dans un objet programmable. Au lieu d'avoir un InventoryManager sur un MonoBehaviour DontDestroyOnLoad, essayez de le mettre sur un ScriptableObject.

Comme il n'est pas lié à la scène, il n'a pas de Transform et ne reçoit pas les fonctions Update, mais il maintiendra l'état entre les chargements de scène sans aucune initialisation spéciale. Préférez une référence publique à votre objet de système d'inventaire plutôt qu'un singleton lorsque vous avez besoin d'un script pour accéder à l'inventaire. Cela facilite le remplacement d'un inventaire de test ou d'un inventaire de tutoriel que si vous utilisiez un singleton.

Ici, vous pouvez imaginer un script Player prenant une référence au système d'inventaire. Quand le joueur avance, le script interroge l'inventaire pour connaître les objets possédés et faire suivre les équipements. L'interface utilisateur d'équipement peut également référencer l'inventaire et parcourir les éléments pour déterminer quoi dessiner.

Concevez votre code avec des objets programmables pour des modifications et un débogage efficaces | Unity