Cette page explique comment utiliser les ScriptableObjects comme conteneurs de données qui séparent les données de la logique dans votre code de jeu.
Il s'agit du deuxième d'une série de six mini-guides créés pour aider les développeurs Unity avec la démo qui accompagne le livre électronique, Créer une architecture de jeu modulaire dans Unity avec ScriptableObjects.
La démo s'inspire des mécanismes classiques des jeux d'arcade à balles et à raquettes et montre comment ScriptableObjects peut vous aider à créer des composants testables, évolutifs et conviviaux pour les concepteurs.
Ensemble, le livre électronique, le projet de démonstration et ces mini-guides fournissent les meilleures pratiques pour utiliser les modèles de conception de programmation avec la classe ScriptableObject dans votre projet Unity. Ces conseils peuvent vous aider à simplifier votre code, à réduire l'utilisation de la mémoire et à favoriser la réutilisabilité du code.
Cette série comprend les articles suivants :
- Commencez avec la démo Unity ScriptableObjects
- Utilisez des énumérations basées sur ScriptableObject dans votre projet Unity
- Utiliser ScriptableObjects comme objets délégués
- Utiliser des ScriptableObjects comme canaux d'événements dans le code du jeu
- Comment utiliser un ensemble d'exécution basé sur ScriptableObject
Avant de vous plonger dans le projet de démonstration ScriptableObject et cette série de mini-guides, n'oubliez pas qu'à la base, les modèles de conception ne sont que des idées. Ils ne s’appliqueront pas à toutes les situations. Ces techniques peuvent vous aider à apprendre de nouvelles façons de travailler avec Unity et ScriptableObjects.
Chaque modèle a des avantages et des inconvénients. Choisissez uniquement ceux qui profitent de manière significative à votre projet spécifique. Vos concepteurs s’appuient-ils fortement sur l’éditeur Unity ? Un modèle basé sur ScriptableObject pourrait être un bon choix pour les aider à collaborer avec vos développeurs.
En fin de compte, la meilleure architecture de code est celle qui convient à votre projet et à votre équipe.
Les développeurs de logiciels sont souvent préoccupés par la modularité, c’est-à-dire par la décomposition d’une application en unités plus petites et autonomes. Chaque module devient responsable d'un aspect spécifique de la fonctionnalité de l'application.
Dans Unity, les ScriptableObjects peuvent aider à séparer les données de la logique.
Les ScriptableObjects excellent dans le stockage de données, en particulier lorsqu'elles sont statiques. Cela les rend idéaux pour les statistiques de jeu, les valeurs de configuration des objets ou des PNJ, les dialogues des personnages et bien plus encore.
Isoler les données de jeu de la logique comportementale peut rendre chaque partie indépendante du projet plus facile à tester et à maintenir. Cette « séparation des préoccupations » peut réduire les effets secondaires imprévus et indésirables lorsque vous apportez les changements nécessaires.
Si vous souhaitez un rappel sur le workflow ScriptableObject, cet article Unity Learn peut vous aider. Sinon, voici une explication rapide :
Définir un objet scriptable : Pour en créer un, définissez une classe C# qui hérite de la classe de base ScriptableObject avec des champs et des propriétés pour les données que vous souhaitez stocker. Les ScriptableObjects peuvent stocker les mêmes types de données disponibles pour MonoBehaviours, ce qui en fait des conteneurs de données polyvalents. Ajoutez l' attribut CreateAssetMenuAttribute depuis l'éditeur pour faciliter la création de l'actif dans le projet.
Créer un actif : Une fois que vous avez défini une classe ScriptableObject, vous pouvez créer une instance de ce ScriptableObject dans le projet. Cela apparaît comme un élément enregistré sur le disque que vous pouvez réutiliser dans différents GameObjects et scènes.
Définir les valeurs : Après avoir créé l’actif, remplissez-le avec des données en définissant les valeurs de ses champs et propriétés dans l’inspecteur.
Utilisez l'actif : Une fois que l'actif contient des données, référencez-les à partir d'une variable ou d'un champ. Toutes les modifications apportées à l’actif ScriptableObject seront répercutées sur l’ensemble du projet.
Vous pouvez réutiliser les ScriptableObjects comme conteneurs de données dans différentes parties de votre jeu. Par exemple, vous pouvez définir les propriétés d'une arme ou d'un personnage dans un ScriptableObject, puis référencer cet élément depuis n'importe où dans le projet.
Remarque : Vous pouvez également générer des ScriptableObjects au moment de l'exécution via la méthode CreateInstance . Cependant, pour le stockage des données, vous créerez généralement les ressources ScriptableObject à l'avance à l'aide de CreateAssetMenuAttribute.
Pour mieux comprendre pourquoi les ScriptableObjects sont un choix plus approprié pour le stockage de données que les MonoBehaviours, comparez les versions vides de chacun. Assurez-vous de définir la sérialisation de votre actif sur Mode: Texte de force dans les paramètres du projet pour afficher le balisage YAML sous forme de texte.
Créez un nouveau GameObject avec un MonoBehaviour par ailleurs vide. Ensuite, comparez cela avec un asset ScriptableObject vide. En les plaçant côte à côte, ils devraient ressembler à la comparaison montrée dans l’image ci-dessus.
Les ScriptableObjects sont plus légers que les MonoBehaviours et ne supportent pas la surcharge associée à ces derniers, comme le composant Transform. Cela donne aux ScriptableObjects une empreinte mémoire plus petite et les rend plus optimisés pour le stockage de données.
Les ScriptableObjects sont enregistrés en tant qu'actifs, ils persistent donc en dehors du mode Lecture, ce qui peut être utile. Par exemple, les données ScriptableObject sont disponibles partout, même si vous chargez une nouvelle scène.
L'exemple de démonstration Patterns présente un écran de crédits de base que vous pouvez tester vous-même. Modifiez le ScriptableObject Credits_Data, puis appuyez sur Mettre à jour pour voir le texte stocké apparaître.
Si vous possédez un RPG avec une grande quantité de dialogues ou une scène de didacticiel avec un script pré-établi, c'est un moyen courant de stocker beaucoup de données.
Alors que les données dans le ScriptableObject sont mises à jour instantanément lorsqu'elles sont modifiées, notre projet nécessite un bouton Mettre à jour pour actualiser l'écran manuellement. L'écran basé sur la boîte à outils de l'interface utilisateur ne se construit qu'une seule fois et doit être averti lorsque les données ont été modifiées.
Créez un événement dans le ScriptableObject si vous souhaitez synchroniser automatiquement les mises à jour. Par exemple, ce script ExampleSO appellerait l'événement OnValueChanged chaque fois que ExampleValue change. Regardez l'exemple de code ci-dessous.
Ensuite, demandez à votre objet d’interface utilisateur d’écoute de s’abonner à OnValueChanged et de le mettre à jour en conséquence.
Les ScriptableObjects brillent lorsque de nombreux objets partagent les mêmes données. Par exemple, si vous créez un jeu de stratégie dans lequel de nombreuses unités ont la même vitesse d'attaque et la même santé maximale, il est inefficace de stocker ces valeurs individuellement sur chaque GameObject.
Au lieu de cela, vous pouvez consolider les données partagées dans un emplacement central et faire en sorte que chaque objet fasse référence à cet emplacement partagé. Dans la conception de logiciels, il s'agit d'une optimisation connue sous le nom de modèle poids mouche. Restructurer votre code de cette manière évite de copier beaucoup de valeurs et réduit votre empreinte mémoire.
Dans PaddleBallSO, le GameDataSO ScriptableObject agit comme un stockage de données partagé.
Plutôt que de conserver une copie séparée des paramètres communs (vitesse, masse, rebond physique, etc.), les scripts Paddle et Ball référencent la même instance GameDataSO lorsque cela est possible. Chaque élément de jeu conserve des données uniques telles que les positions et les événements d'entrée, mais utilise par défaut des données partagées lorsque cela est possible.
Même si les économies de mémoire ne sont peut-être pas perceptibles avec seulement deux ou trois objets, la modification des données partagées est plus rapide et moins sujette aux erreurs que la modification manuelle de chaque objet.
Par exemple, si vous devez modifier la vitesse de la palette, l'ajuster à un seul endroit met à jour les deux palettes dans chaque scène. Si vous les stockez en tant que champs uniques dans les MonoBehaviours, un clic incorrect pourrait facilement désynchroniser deux valeurs.
Le déchargement de données dans ScriptableObjects peut également aider au contrôle des versions et éviter les conflits de fusion lorsque les coéquipiers travaillent sur la même scène ou sur le même préfab.
Le GameDataSO montre comment utiliser un ScriptableObject comme conteneur de données. Dans PaddleBallSO, cela inclut divers paramètres pour configurer le gameplay :
- Données de la pagaie : Des attributs tels que la vitesse, la traînée et la masse des pagaies déterminent le mouvement et la physique des pagaies pendant le jeu.
- Données de la balle : Cela contient la vitesse actuelle de la balle, la vitesse maximale et le multiplicateur de rebond, qui contrôle le comportement de la balle lorsqu'elle interagit avec une simulation.
- Données du match : GameDataSO contient des informations sur les délais entre les points au cours d'un match, aidant à contrôler le rythme du jeu.
- ID de joueur : Les objets scriptables PlayerIDSO fonctionnent comme une identification d'équipe pour chaque joueur (par exemple, Player1 et Player2).
- Sprites de joueur : Ces sprites optionnels permettent de personnaliser l'avatar du joueur.
- Disposition des niveaux : L'objet LevelLayoutSO définit les positions de départ des joueurs et des éléments de jeu comme les buts et les murs.
Avec ces paramètres et ces données regroupés dans un emplacement central, GameDataSO permet à n'importe quel objet d'accéder à ces données partagées. Cela simplifie la façon dont vous gérez ces objets et favorise une plus grande cohérence tout au long de votre projet. Changer la physique des pagaies ? Effectuez une seule modification ici au lieu d’ajuster plusieurs scripts.
Parfois, on peut avoir le beurre et l’argent du beurre. Avec la double sérialisation, vous pouvez stocker des données dans un ScriptableObject tout en les conservant simultanément dans un autre format.
Le script LevelLayoutSO illustre ce concept. En plus de contenir les positions de départ des pagaies et de la balle, il stocke les données de transformation des murs et des buts dans une structure personnalisée.
Ces valeurs peuvent être écrites sur le disque via la méthode ExportToJson . Les fichiers JSON sont des textes lisibles par l’homme, permettant une modification simple en dehors d’Unity. Cela vous permet de travailler avec des ScriptableObjects dans l'éditeur, puis de stocker leurs données dans un autre emplacement, comme un fichier JSON ou XML.
Les formats de fichiers tels que JSON et XML peuvent être difficiles à utiliser dans l'éditeur, mais ils sont faciles à modifier en dehors d'Unity dans un éditeur de texte. Cela ouvre la possibilité de niveaux personnalisés ou modifiés par l'utilisateur.
Le script GameSetup peut ensuite utiliser soit un LevelLayout ScriptableObject, soit un fichier JSON externe pour générer le niveau de jeu.
Pour charger un niveau personnalisé, le script d'installation génère un ScriptableObject au moment de l'exécution avec CreateInstance. Ensuite, il lit le texte du fichier JSON pour remplir le ScriptableObject.
Vos données personnalisées remplacent le contenu du ScriptableObject et vous permettent d'utiliser ce niveau modifié en externe comme n'importe quel autre. Le reste de l'application fonctionne normalement, sans tenir compte du changement.
Bien que notre mini-jeu de paddle ball ne puisse pas démontrer tous les cas d'utilisation des conteneurs de données ScriptableObject, tenez compte des éléments suivants pour vos propres applications :
- Configuration du jeu : Pensez aux constantes, aux règles du jeu ou à tout autre paramètre de configuration qui n'a pas besoin de changer pendant le jeu. D’autres composants peuvent ensuite faire référence à ces données de configuration sans utiliser de valeurs codées en dur.
- Attributs du personnage et de l'ennemi : Utilisez ScriptableObjects pour définir des attributs tels que la santé, la puissance d'attaque, la vitesse, etc. Cela permet à vos concepteurs d’équilibrer et d’ajuster les éléments de jeu sans développeur.
- Systèmes d'inventaire et d'articles : Les définitions d'éléments et les propriétés telles que les noms, les descriptions et les icônes sont parfaites pour les ScriptableObjects. Vous pouvez également les utiliser dans le cadre d'un système de gestion d'inventaire pour suivre les objets que le joueur collecte, utilise ou équipe.
- Dialogue et systèmes narratifs : Les ScriptableObjects peuvent stocker du texte de dialogue, des noms de personnages, des chemins de dialogue ramifiés et d'autres données liées au récit. Ils peuvent jeter les bases de systèmes de dialogue complexes.
- Données de niveau et de progression : Vous pouvez utiliser des ScriptableObjects pour définir les dispositions des niveaux, les points d'apparition des ennemis, les objectifs et d'autres informations liées aux niveaux.
- Extraits audio : Comme vu dans le projet PaddleBallSO , les ScriptableObjects peuvent stocker un ou plusieurs clips audio. Ceux-ci peuvent définir des effets audio ou de la musique dans plusieurs parties de votre jeu.
- Extraits d'animation: Les ScriptableObjects peuvent être utilisés pour stocker des clips d'animation, ce qui est utile pour définir des animations communes partagées entre plusieurs GameObjects ou personnages.
Au fur et à mesure que vous approfondissez les ScriptableObjects et que vous les adaptez à vos propres projets, vous découvrirez encore plus d'applications pour eux. Ils sont particulièrement utiles pour gérer les données et facilitent le maintien de la cohérence entre les différents éléments du jeu.
Pour en savoir plus sur les modèles de conception avec ScriptableObjects, consultez le livre électronique Créer une architecture de jeu modulaire dans Unity avec ScriptableObjects. Vous pouvez également en savoir plus sur les modèles de conception de développement Unity courants dans Améliorez votre code avec les modèles de programmation de jeux.