Techniques d'intégration multimédia
Supposons que dans un jeu, je veux qu'un objet qui a un script Personnage.cs puisse "parler" à un autre objet qui a un script GameManager.cs.
Comment je peux m'arranger pour que mon objet Personnage puisse communiquer avec GameManager?
C'est l'approche la plus utilisée.
Dans le script du Personnage, on fait un champ [SerializeField] qui va permettre d'associer GameManager au script du Personnage dans l'inspecteur:
[SerializeField] private GameManager _gm;
Plusieurs instructions dans Unity permettent d'obtenir une référence à un objet qui est sur la scène en le cherchant (par son nom, son type, son Tag ou même selon les composants qui lui sont associés).
Quelques exemplesp>
// exemple 1: Associer le script de GameManager au champ _gm en faisant une recherche avec son nom
GameObject gObjet = GameObject.Find("GameManager"); // trouve le gameObject dont le nom est "GameManager"
_gm = _gObjet.GetComponent<GameManager>(); // Associe le composant de script de cet objet à notre variable _gm (de type GameManager)
// exemple 2: Associer le script de GameManager au champ _gm en faisant une recherche avec son type (son script)
_gm = (GameManager) FindObjectOfType(typeof(GameManager));
// exemple 3: En supposant qu'on a donné un tag "TagJoueur" à notre Personnage,
// on peut dans GameManager l'associer à un champ de type Personnage
// en faisant une recherche par Tag
GameObject gObjet = GameObject.FindGameObjectWithTag("TagJoueur" ); // trouve le gameObject dont le tag est "TagJoueur"
_perso = _gObjet.GetComponent<Personnage>(); // Associe le composant de script de cet objet à notre variable _gm (de type Personnage)
C'est une approche avancée et qui offre de grands avantages. Le principe général est de s'arranger pour qu'un gameObject soit accessible de façon statique, c'est à dire de faire référence à lui directement par le nom de sa classe (son script), et ce à partir de n'importe quel script de notre projet.
Étape 1: L'accès statique
Pour commencer, il faut mettre dans le code de ce gameObject (disons GameManager.cs) une variable static. Souvent, on nomme cette variable _instance car elle va faire référence à notre instance d'objet (le GameManager qui est sur la scène dans notre exemple).
private static GameManager _instance;
Puis on stocke la référence de cet objet dans _instance dès sa création (on préfére souvent Awake() à Start() pour cette opération):
void Awake()
{
_instance = this;
}
Comme on veut accéder à _instance de l'extérieur et que ce champ est privé, il faut aussi faire un getter:
public static GameManager instance
{
get{ return _instance; }
}
Dès maintenant, il sera possible de "parler" à GameManager à partir de n'importe quel script en passant par ce getter statique.
Par exemple, dans le script Personnage.cs j'accède à la fonction publique AjouterPoint() de GameManager en faisant:
GameManager.instance.AjouterPoints();
Étape 2: Le Singleton
Un Singleton désigne un mécanisme de programmation qui empêche un objet d'exister en plusieurs exemplaires. Une fois instancié, l'objet est unique et aucun objet ne pourra plus être instancié avec sa classe.
Les objets pour lesquels on créé un accès statique comme à l'étape 1 doivent être uniques dans un projet, puisque la variable statique _instance, de par sa nature, ne peut faire référence qu'à un seul objet à la fois. Si on ne met pas en place une mécanique de Singleton, on courre le risque de créer un jour par erreur un autre exemplaire de GameManager et se retrouver avec 2 GameManagers qui agissent en même temps et notre _instance ne pourra faire référence qu'à un seul des 2...
Le Singleton fait en sorte que si Unity tente d'instancier (probablement par erreur) un 2e GameManager, ce dernier s'auto-détruit, laissant le GameManager original et sa valeur _instance intacts.
Pour faire un Singleton, modifier le code du Awake() de votre script ainsi:
void Awake()
{
if(_instance==null) // Si Aucune instance n'a été créée à date...
{
_instance = this; // Associe "moi" à cette variable _instance
}
else // Sinon (si une instance existe déjà)
{
Destroy(gameObject); // Détruit l'objet courrant (pas de nouvelle instance donc!)
}
}
Avantage(s):
Inconvénient(s):
Le principe ici, c'est qu'un objet fournisse une référence à un autre objet, souvent au moment même de son instanciation (ou immédiatement après). On va communiquer ainsi à un objet un "lien" permettant de contacter un autre objet.
Par exemple, disons que GameManager doit faire apparaître un ennemi par prog (prefab monstreBleu avec un script Ennemi.cs) et doit s'arranger pour que cet ennemi puisse communiquer avec GameManager plus tard. Dans le but d'activer une fonction publique AjoutPoints() par exemple.
Dans le script Ennemis.cs on mettra:
private GameManager _gm;
public void Init(GameManager refGM)
{
_gm = refGM;
}
Et dans GameManager.cs on mettra:
Ennemi mechantMonstre = Instantiate(_prefabMonstre, Vector3.zero, Quaternion.identity); // Instanciation d'un ennemi
mechantMonstre.Init( this ); // Cet ennemi se fait "injecter" une référence pour l'objet GameManager
À partir de ce moment, mon mechantMonstre possède une référence à GameManager dans sa variable _gm et pourra lui "parler" directement en faisant:
_gm.AjoutPoints();
Inconvénient(s):
Remarque: Une 5e approche est possible pour référer à un gameObject dans Unity!
Elle fait appel à un type d'objet très particulier appelé ScriptableObject, qui pourrait essentiellement jouer un peu le rôle d'un Singleton par exemple.
Cette approche est pratique et utilisée par beaucoup de programmeurs Unity. Cependant, elle n'est pas abordée dans le cadre de ce cours.
Si cela vous intéresse, voici un lien pour une capsule à ce sujet réalisée par les développeurs de Unity https://youtu.be/WLDgtRNK2VE