Que recherchez-vous ?

6 façons dont les ScriptableObjects peuvent bénéficier à votre équipe et à votre code

THOMAS KROGH-JACOBSEN / UNITY TECHNOLOGIESProduct Marketing Core Tech
Apr 20, 2023|17 Min
6 façons dont les ScriptableObjects peuvent bénéficier à votre équipe et à votre code
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.

Nous sommes heureux d'annoncer le lancement d'un nouvel e-book technique, Créer une architecture de jeu modulaire dans Unity avec ScriptableObjectsqui fournit les meilleures pratiques de développeurs professionnels pour le déploiement de ScriptableObjects en production.

Avec le livre électronique, vous pouvez télécharger un projet de démonstration sur GitHub, inspiré par les mécanismes des jeux d'arcade classiques à base de balles et de pagaies. La démo montre comment ScriptableObjects peut vous aider à créer des composants testables et évolutifs, tout en étant conviviaux pour les concepteurs. Bien qu'un jeu comme celui-ci puisse être construit avec beaucoup moins de lignes de code, cette démo montre les ScriptableObjects en action.

Une démonstration de balle et de pagaie accompagne l'e-book.
Une démonstration de balle et de pagaie accompagne l'e-book.

Ce billet explique les avantages des ScriptableObjects, mais ne couvre pas les bases ou le codage général dans Unity. Si vous êtes novice en matière de programmation dans Unity, rendez-vous sur le site Unity Learn, qui propose des tutoriels d'introduction très utiles. Le premier chapitre de l'e-book propose également une solide introduction.

Examinons six façons dont vous pouvez tirer profit de l'utilisation de ScriptableObjects dans vos projets. Vous voulez en savoir plus ? Tous ces exemples sont approfondis dans le livre électronique et le projet de démonstration.

1. Collaboration efficace au sein de l'équipe

Bien que de nombreuses techniques présentées ici puissent également être réalisées à l'aide de classes C#, l'un des principaux avantages des ScriptableObjects est leur accessibilité pour les artistes et les concepteurs. Ils peuvent utiliser les objets scriptables pour configurer et appliquer la logique du jeu dans un projet sans avoir à modifier le code.

L'éditeur permet de visualiser et de modifier facilement les objets scriptables, ce qui permet aux concepteurs de mettre en place des données de jeu sans avoir besoin d'un soutien important de la part de l'équipe de développement. Cela s'applique également à la logique de jeu, comme l'application d'un comportement à un PNJ par l'ajout d'un objet scriptable (expliqué dans les modèles ci-dessous).

Le stockage des données et de la logique sur un seul MonoBehaviour peut entraîner des conflits de fusion fastidieux si deux personnes modifient des parties différentes du même préfabriqué ou de la même scène. En divisant les données partagées en fichiers et actifs plus petits grâce aux objets scriptables, les concepteurs peuvent développer le gameplay en parallèle avec les développeurs, au lieu d'attendre que ces derniers aient fini de configurer le gameplay dans le code avant de le tester.

Des problèmes peuvent survenir lorsque des collègues ayant des rôles différents accèdent en même temps au code et aux ressources du jeu. Avec les objets scriptables, le programmeur peut contrôler quelle partie du projet est modifiable dans l'éditeur. En outre, l'utilisation de ScriptableObjects pour organiser votre code conduit naturellement à une base de code plus modulaire et plus efficace à tester.

Découvrez comment un concepteur de jeux utilise les objets scriptables (ScriptableObjects)

Christo Nobbs, concepteur de jeux techniques senior, spécialisé dans la conception de jeux systémiques et dans Unity (C#), a participé à la rédaction de The Unity game designer playbooket est l'auteur principal d'une série d'articles de blog sur la conception de systèmes de jeu dans Unity. Ses billets, "Des systèmes qui créent des écosystèmes : Conception de jeux émergents" et "Un plaisir imprévisible : La valeur de la randomisation dans la conception des jeux" fournit des exemples intéressants de la manière dont les concepteurs peuvent utiliser les ScriptableObjects.

2. Conteneurs de données

La modularité est un principe logiciel général qui peut être mis en œuvre en C# sans utiliser de ScriptableObjects. Mais, comme nous l'avons mentionné plus haut, les ScriptableObjects contribuent à promouvoir des pratiques de codage propres en séparant les données de la logique, ce qui constitue un premier pas vers un code de jeu modulaire. Cette séparation signifie qu'il est plus facile d'apporter des modifications sans provoquer d'effets secondaires involontaires et améliore la testabilité.

Les ScriptableObjects excellent dans le stockage de données statiques, ce qui les rend pratiques pour configurer des valeurs de jeu statiques comme les objets ou les statistiques des PNJ, le dialogue des personnages, et bien d'autres choses encore. Les ScriptableObjects étant sauvegardés en tant qu'actifs, ils persistent en dehors du mode jeu, ce qui permet de les utiliser pour le chargement d'une configuration statique qui change dynamiquement au moment de l'exécution.

Bien que les modifications apportées aux données des objets scriptables persistent dans l'éditeur, il est important de noter qu'elles ne sont pas conçues pour sauvegarder les données du jeu. Dans ce cas, il est préférable d'utiliser un système de sérialisation, tel que JSON, XML, ou une solution binaire si les performances sont critiques.

Les MonoBehaviours entraînent une surcharge supplémentaire puisqu'ils nécessitent un GameObject - et par défaut un Transform - pour jouer le rôle d'hôte. Cela signifie que vous devez créer un grand nombre de données inutilisées avant de stocker une seule valeur. Un ScriptableObject réduit cette empreinte mémoire et supprime le GameObject et le Transform. Il stocke également les données au niveau du projet, ce qui est utile si vous devez accéder aux mêmes données à partir de plusieurs scènes.

Il est courant d'avoir de nombreux GameObjects qui s'appuient sur des données dupliquées qui n'ont pas besoin d'être modifiées au moment de l'exécution. Plutôt que d'avoir ces données locales dupliquées sur chaque GameObject, vous pouvez les canaliser dans un ScriptableObject. Chacun des objets stocke une référence à la ressource de données partagée, plutôt que de copier les données elles-mêmes. Cela permet d'améliorer considérablement les performances des projets comportant des milliers d'objets.

Un grand nombre d'objets avec des données locales dupliquées entraîne des problèmes de performance.
De nombreux objets avec des données locales dupliquées entraînent des inefficacités au niveau des performances.
Plusieurs objets partagent des données (plutôt que de les dupliquer) par l'intermédiaire d'un ScriptableObject.
Plusieurs objets partagent des données (plutôt que de les dupliquer) par l'intermédiaire d'un ScriptableObject.

Dans la conception de logiciels, il s'agit d'une optimisation connue sous le nom de " flyweight pattern". En restructurant votre code de cette manière à l'aide de ScriptableObjects, vous évitez de copier des valeurs et vous réduisez votre empreinte mémoire. Consultez notre e-book, Améliorez votre code avec les modèles de programmation de jeux pour en savoir plus sur l'utilisation des modèles de conception dans Unity.

3. Enums

Un bon exemple de la façon dont les ScriptableObjects peuvent simplifier votre code est de les utiliser comme des enums pour les opérations de comparaison. Le ScriptableObject peut représenter une catégorie ou un type d'objet, tel qu'un effet de dégâts spéciaux - froid, chaleur, électricité, magie, etc.

Si votre application nécessite un système d'inventaire pour équiper les objets du jeu, les ScriptableObjects peuvent représenter des types d'objets ou des emplacements d'armes. Les champs de l'inspecteur fonctionnent alors comme une interface "glisser-déposer" pour les configurer.

Catégories basées sur des objets scriptables (glisser-déposer)
Catégories basées sur des objets scriptables (glisser-déposer)

L'utilisation des ScriptableObjects en tant qu'enums devient plus intéressante lorsque l'on souhaite les étendre et ajouter des données supplémentaires. Contrairement aux enums normaux, les ScriptableObjects peuvent avoir des champs et des méthodes supplémentaires. Il n'est pas nécessaire de disposer d'une table de recherche distincte ou d'établir une corrélation avec un nouveau tableau de données.

Alors que les enums traditionnels ont un ensemble fixe de valeurs, les enums ScriptableObject peuvent être créés et modifiés au moment de l'exécution, ce qui permet d'ajouter ou de supprimer des valeurs en fonction des besoins.

Si vous disposez d'une longue liste de valeurs d'énumération sans numérotation explicite, l'insertion ou la suppression d'une énumération peut en modifier l'ordre. Cette réorganisation peut introduire des bogues subtils ou des comportements non souhaités. Les énumérations basées sur les objets scriptables ne posent pas ces problèmes. Vous pouvez supprimer ou ajouter des éléments à votre projet sans devoir modifier le code à chaque fois.

Supposons que vous souhaitiez rendre un objet équipable dans un jeu de rôle. Vous pourriez ajouter un champ booléen supplémentaire au ScriptableObject pour ce faire. Certains personnages ne sont-ils pas autorisés à détenir certains objets ? Certains objets sont-ils magiques ou ont-ils des capacités spéciales ? Les énumérations basées sur les objets scriptables peuvent le faire.

4. Objets délégués

Étant donné que vous pouvez créer des méthodes sur un objet scriptable, ces objets sont aussi utiles pour contenir de la logique ou des actions que pour contenir des données. En déplaçant la logique de votre MonoBehaviour vers un ScriptableObject, vous pouvez utiliser ce dernier comme objet délégué, ce qui rend le comportement plus modulaire.

Si vous devez effectuer des tâches spécifiques, vous pouvez encapsuler leurs algorithmes dans leurs propres objets. Le Gang des quatre original fait référence à cette conception générale en tant que modèle de stratégie. L'exemple ci-dessous montre comment rendre le modèle de stratégie plus utile en utilisant une classe abstraite pour implémenter EnemyAI. Il en résulte plusieurs ScriptableObjects dérivés avec un comportement différent, qui devient alors un comportement enfichable puisque chaque actif est interchangeable. Il suffit de glisser-déposer l'objet scriptable de votre choix dans le MonoBehaviour.

Les comportements enfichables peuvent être modifiés au moment de l'exécution ou dans l'éditeur.
Les comportements enfichables peuvent être modifiés au moment de l'exécution ou dans l'éditeur.

Pour un exemple détaillé montrant comment utiliser les ScriptableObjects pour piloter le comportement, regardez la série de vidéos Pluggable AI with ScriptableObjects. Ces sessions présentent un système d'IA basé sur une machine à états finis qui peut être configuré à l'aide de ScriptableObjects pour les états, les actions et les transitions entre ces états.

5. Canaux d'événements

Dans les projets de grande envergure, il est fréquent que plusieurs GameObjects doivent partager des données ou des états en évitant les références directes entre ces objets. La gestion de ces dépendances à grande échelle peut nécessiter des efforts considérables et constitue souvent une source de bogues. De nombreux développeurs utilisent des singletons - une instance globale d'une classe qui survit au chargement de la scène. Cependant, les singletons introduisent des états globaux et rendent les tests unitaires difficiles. Si vous travaillez avec un Prefab qui fait référence à un singleton, vous finirez par importer toutes ses dépendances juste pour tester une fonction isolée. Cela rend votre code moins modulaire et moins facile à déboguer.

Une solution consiste à utiliser des événements basés sur des objets scriptables pour aider vos objets de jeu à communiquer. Dans ce cas, vous utilisez les ScriptableObjects pour mettre en œuvre une forme de modèle de conception d'observateur, où un sujet diffuse un message à un ou plusieurs observateurs faiblement découplés. Chaque objet observateur peut réagir indépendamment du sujet mais n'est pas conscient des autres observateurs. Le sujet peut également être appelé "éditeur" ou "diffuseur" et les observateurs "abonnés" ou "auditeurs".

Vous pouvez mettre en œuvre le modèle d'observateur avec des MonoBehaviours ou des objets C#. Bien qu'il s'agisse déjà d'une pratique courante dans le développement Unity, une approche basée uniquement sur les scripts signifie que vos concepteurs dépendront de l'équipe de programmation pour chaque événement nécessaire au cours du jeu.

Le canal d'événements basé sur les objets scriptables
Le canal d'événements basé sur les objets scriptables

À première vue, il semble que vous ayez ajouté une couche de frais généraux au modèle d'observateur, mais cette structure présente certains avantages. Les ScriptableObjects étant des actifs, ils sont accessibles à tous les objets de votre hiérarchie et ne disparaissent pas au chargement de la scène.

L'accès facile et permanent à certaines ressources est la raison pour laquelle de nombreux développeurs utilisent des singletons. Les objets scriptables peuvent souvent offrir les mêmes avantages sans introduire autant de dépendances inutiles.

Dans les événements basés sur des objets scriptables, n'importe quel objet peut servir d'éditeur (qui diffuse l'événement) et n'importe quel objet peut servir d'abonné (qui écoute l'événement). Le ScriptableObject se situe au milieu et aide à relayer le signal, agissant comme un intermédiaire centralisé entre les deux.

Il s'agit en quelque sorte d'un "canal d'événements". Imaginez le ScriptableObject comme une tour de radio dont les signaux sont écoutés par un nombre quelconque d'objets. Un MonoBehaviour intéressé peut s'abonner au canal d'événements et réagir lorsque quelque chose se produit.

La démo montre comment le modèle d'observateur vous aide à mettre en place des événements de jeu pour l'interface utilisateur, les sons et les scores.

6. Ensembles d'exécution

Au moment de l'exécution, vous aurez souvent besoin de suivre une liste d'objets de jeu ou de composants dans votre scène. Par exemple, une liste d'ennemis est un élément auquel vous devez accéder fréquemment, mais il s'agit également d'une liste dynamique qui change au fur et à mesure que des ennemis apparaissent ou sont vaincus. Le singleton offre un accès global facile, mais il présente plusieurs inconvénients. Au lieu d'utiliser un singleton, envisagez de stocker des données sur un ScriptableObject sous la forme d'un "Runtime Set". L'instance ScriptableObject apparaît au niveau du projet, ce qui signifie qu'elle peut stocker des données disponibles pour n'importe quel objet de n'importe quelle scène, offrant ainsi un accès global similaire. Comme les données se trouvent sur un bien, la liste publique des éléments est accessible à tout moment.

Dans ce cas d'utilisation, vous obtenez un conteneur de données spécialisé qui maintient une collection publique d'éléments, mais qui fournit également des méthodes de base pour ajouter et retirer des éléments de la collection. Cela permet de réduire le nombre de singletons et d'améliorer la testabilité et la modularité.

Un Runtime Set fournit un accès global à une collection de données.
Un Runtime Set fournit un accès global à une collection de données

La lecture des données directement à partir d'un ScriptableObject est également plus optimale que la recherche dans la hiérarchie de la scène avec une opération de recherche telle que Object.FindObjectOfType ou GameObject.FindWithTag. En fonction de votre cas d'utilisation et de la taille de votre hiérarchie, ces méthodes sont relativement coûteuses et peuvent s'avérer inefficaces pour les mises à jour par image.

Il existe plusieurs cadres ScriptableObjects qui offrent davantage de cas d'utilisation que ces six scénarios. Certaines équipes décident d'utiliser les ScriptableObjects de manière intensive, tandis que d'autres limitent leur utilisation au chargement de données statiques et à la séparation de la logique et des données. En fin de compte, ce sont les besoins de votre projet qui détermineront l'usage que vous en ferez.

Série avancée pour les programmeurs Unity

Créer une architecture de jeu modulaire dans Unity avec ScriptableObjects est le troisième guide de notre série destinée aux programmeurs Unity de niveau intermédiaire à avancé. Chaque guide, rédigé par des programmeurs expérimentés, présente les meilleures pratiques sur des sujets importants pour les équipes de développement.

Créer un guide de style C# : Écrire un code plus propre et évolutif vous aide à développer un guide de style pour unifier votre approche et créer une base de code plus cohérente.

Améliorez votre code avec des modèles de programmation de jeuxmet en évidence les meilleures pratiques pour utiliser les principes SOLID et les modèles de programmation courants afin de créer une architecture de code de jeu évolutive dans votre projet Unity.

Nous avons créé cette série pour fournir des conseils pratiques et de l'inspiration à nos créateurs expérimentés, mais il ne s'agit pas d'un livre de règles. Il existe de nombreuses façons de structurer votre projet Unity et ce qui peut sembler naturel pour une application peut ne pas l'être pour une autre. Évaluez les avantages et les inconvénients de chaque recommandation, conseil et modèle avec vos collègues avant de les mettre en œuvre.

Vous trouverez des guides et des articles plus avancés sur le hub des meilleures pratiques Unity.

Couverture du livre électronique pour "Create modular game architecture in Unity with ScriptableObjects" (Créer une architecture de jeu modulaire dans Unity avec ScriptableObjects)
"Créer une architecture de jeu modulaire dans Unity avec ScriptableObjects" ebook