Hero image

Comment utiliser un ensemble d'exécution basé sur des objets scriptables ?

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.

Note importante avant de commencer

Avant de vous plonger dans le projet de démonstration ScriptableObject et dans cette série de mini-guides, n'oubliez pas qu'à la base, les modèles de conception ne sont que des idées. Elles ne s'appliquent pas à toutes les situations. Ces techniques peuvent vous aider à apprendre de nouvelles façons de travailler avec Unity et ScriptableObjects.

Chaque modèle présente des avantages et des inconvénients. Ne choisissez que ceux qui bénéficient de manière significative à votre projet spécifique. Vos concepteurs utilisent-ils beaucoup 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 s'adapte à votre projet et à votre équipe.

Jeux d'exécution

Les objets scriptables peuvent stocker des données statiques. Ils peuvent également être modifiés pour contenir des données dynamiques. Ceci est particulièrement utile pour référencer une collection de GameObjects ou de composants au moment de l'exécution.

Un "runtime set" comme celui-ci reproduit la raison pour laquelle certains développeurs utilisent des singletons - un accès global facile. Malheureusement, les singletons sont accompagnés d'un bagage de dépendances indésirables. Ces dépendances supplémentaires peuvent compliquer les tests et rendre le débogage plus difficile.

Une autre solution consiste à utiliser un ensemble d'exécution basé sur des objets scriptables pour les composants fréquemment référencés. Par exemple, utilisez-les pour suivre les éléments du jeu tels que les ennemis, les objets à collectionner ou les points de contrôle.

En tant qu'actifs au niveau du projet, les ScriptableObjects offrent un accès global aux données de tous les objets d'une scène. Cette caractéristique permet le partage d'informations sans les nombreux inconvénients associés aux singletons.

Mise en œuvre de base

Voici une façon de créer un jeu d'exécution pour les GameObjects. Il s'agit d'un conteneur de données qui conserve une collection publique d'éléments, mais qui comprend également des méthodes permettant d'ajouter et de supprimer des membres.

Au moment de l'exécution, tout MonoBehaviour peut faire référence à la propriété publique Items pour obtenir la liste complète des GameObjects. Un autre script ou composant peut appeler Ajouter ou Supprimer pour modifier la liste des éléments.

using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(menuName = "GameObject Runtime Set", fileName
= "GORuntimeSet")]
public class GameObjectRuntimeSetSO: ScriptableObject
{

   private List<GameObject> items = new List<GameObject>();
   public List<GameObject> Items => items;

   public void Add(GameObject thingToAdd)
   {
       if (!items.Contains(thingToAdd))
            items.Add(thingToAdd);
   }

   public void Remove(GameObject thingToRemove)
   {
       if (items.Contains(thingToRemove))
            items.Remove(thingToRemove);
   } 
}

Version générique

Vous pouvez rendre un runtime set plus spécifique afin qu'il ne suive qu'un type particulier de MonoBehaviour. Dans ce cas, créez des ensembles spécifiques pour chaque type (par exemple, EnemyRuntimeSet, PickupRuntimeSet, etc.).

Une classe abstraite générique peut rationaliser ce processus. Vous pouvez ensuite dériver d'autres ensembles d'exécution à partir de cette classe de base. Par exemple, si vous souhaitez créer un ensemble d'exécution pour un composant Enemy personnalisé, vous pouvez définir des classes pour un RuntimeSetSO<T> générique et un EnemyRuntimeSetSO concret, comme dans l'extrait de code ci-dessous.

Remarquez que la classe concrète, EnemyRuntimeSetSO, ne comporte aucun détail d'implémentation, seulement un attribut CreateAssetMenu.

Chaque nouveau composant que vous souhaitez suivre a simplement besoin d'un ensemble d'exécution correspondant pour le gérer. Créez autant de classes concrètes que nécessaire. Les ennemis, les PNJ, les objets de l'inventaire, les quêtes et bien d'autres choses encore peuvent tous avoir leurs propres jeux de temps d'exécution.

public abstract class RuntimeSetSO<T>: ScriptableObject
{
   public List<T> Items = new List<T>();

   public void Add(T thing)
   {
       if (!Items.Contains(thing))
            Items.Add(thing);
   }

   public void Remove(T thing)
   {
       if (Items.Contains(thing))
            Items.Remove(thing);
   } 
}

[CreateAssetMenu(menuName = "Enemy Runtime Set", fileName =
"EnemyRuntimeSet")]

public class EnemyRuntimeSetSO: RuntimeSet<Enemy>
{

}
Erreur de concordance

Utilisation et limites

La gestion de la durée d'exécution est laissée à votre discrétion. Souvent, vous pouvez utiliser un deuxième MonoBehaviour pour le configurer ou le mettre à jour pendant le jeu.

Si vous souhaitez qu'un composant s'intègre automatiquement dans un ensemble, vous pouvez également invoquer les méthodes Add ou Remove de l'ensemble d'exécution à partir des méthodes OnEnable et OnDisable du MonoBehaviour.

Par exemple, votre classe fictive Enemy pourrait ressembler à l'extrait de code ci-dessous.

Ici, chaque composant ennemi peut s'ajouter ou se supprimer à l'aide de ses méthodes OnEnable ou OnDisable. Si le champ m_RuntimeSet est défini dans l'inspecteur, le composant Enemy apparaîtra automatiquement dans le EnemyRuntimeSetSO. Ceci est particulièrement pratique si vous utilisez le composant Enemy avec des Prefabs.

L'une des limites de cette technique est que si vous inspectez le ScriptableObject au moment de l'exécution, vous ne pourrez pas voir le contenu de la liste des éléments dans l'inspecteur par défaut.

Au lieu de cela, un "Type mismatch" apparaît dans chaque champ de l'élément. Par conception, un ScriptableObject ne peut pas sérialiser un objet de scène. La liste fonctionne, mais les données ne s'affichent pas correctement.

Utilisez une propriété publique ou l'attribut HideInInspector si vous souhaitez éviter toute confusion et empêcher l'affichage de la liste dans l'inspecteur.

Vous pouvez également résoudre ce problème à l'aide d'un script d'éditeur et d'un inspecteur personnalisés. Parmi les bons exemples, citons Soap - ScriptableObject Architecture Pattern dans le Unity Asset Store ou l'exemple de démonstration de Patterns ci-dessous.

public class Enemy: MonoBehaviour
{
     [SerializeField] private EnemyRuntimeSetSO m_RuntimeSet;

     private void OnEnable()
     {
         m_RuntimeSet.Add(this);
     }

     private void OnDisable()
     {
         m_RuntimeSet.Remove(this);
     }
}
Jeux d'exécution

Démonstration de motifs

Bien que PaddleBallSO ne présente pas de cas d'utilisation pour les ensembles d'exécution, la démo Patterns du projet inclut un petit échantillon qui montre ce modèle de conception à l'œuvre.

Cliquez sur le bouton " Spawn Block" pour remplir progressivement le mur de blocs. La balle détruit un bloc au contact.

Sélectionnez Select Set pour afficher le BlockRuntimeSet_Data dans l'inspecteur. Elle contient la liste de tous les blocs de la scène.

Vous pouvez remplir la grille de blocs en continu aussi vite que la balle les enlève. En même temps, l'ensemble de la durée d'exécution suit l'évolution active du nombre de blocs dans la liste des éléments.

La démo Patterns présente quelques améliorations de la qualité de vie lors de l'utilisation d'ensembles d'exécution :

  • Inspecteur personnalisé : Comme les ScriptableObjects ne peuvent pas sérialiser les objets de scène, le script GameObjectRuntimeSetEditor fournit un inspecteur personnalisé qui remplace l'erreur d'incompatibilité de type par défaut. Cliquez sur un bloc dans l'inspecteur pour le mettre en évidence dans la hiérarchie de la scène.
  • Mise à jour des événements : L'ensemble d'exécution possède une action System.Action appelée ItemsChanged. Cela permet de notifier le script de l'éditeur lors de l'ajout ou de la suppression de membres dans la liste des éléments. Cela permet d'actualiser l'inspecteur lorsque la liste des éléments est modifiée. Un canal d'événement synchronise également le nombre actuel d'éléments avec un composant RuntimeSetCounter personnalisé. Cela montre le nombre total de blocs dans l'interface utilisateur à l'écran.
Inspecteur personnalisé

L'utilité des ensembles de règles d'exécution

La prochaine fois que vous aurez besoin d'une liste centralisée d'objets ou de composants de jeu, pensez aux ensembles d'exécution. Ils peuvent être moins coûteux qu'une opération de recherche(Object.FindObjectOfType ou GameObject.FindWithTag) et moins problématiques que les singletons.

En tant qu'objets scriptables, ils sont découplés des objets de jeu individuels dans la scène. Conservez autant de jeux d'exécution que nécessaire pour tout ce que vous souhaitez suivre fréquemment au cours de l'exécution.

Les ensembles d'exécution peuvent simplifier la manière dont vous surveillez les objets de jeu sans encourir de dépendances inutiles. Vous pouvez utiliser le temps gagné lors du débogage et des tests pour développer de nouvelles expériences de jeu pour vos joueurs.

outro scriptable

Plus de ressources ScriptableObject

Nous espérons que ces ensembles de durée d'exécution seront utiles pour vos projets à venir.

Pour en savoir plus sur les modèles de conception avec ScriptableObjects, consultez notre e-book technique, Créer une architecture de jeu modulaire dans Unity avec ScriptableObjects. Vous pouvez également en savoir plus sur les modèles de conception courants du développement Unity dans la section Améliorez votre code avec des modèles de programmation de jeux.

Vous avez aimé ce contenu ?