Les types et classes incontournables d’Unity 3D

Cet article est un inventaire de différentes classes utilisables dans Unity 3D, lors de la réalisation de scripts en C#. Cet inventaire n’est pas exhaustif (car il y a beaucoup de classes dans le moteur !), son intérêt est surtout de décrire brièvement des possibilités présentes dans Unity.

Types de bases du C#

Il s’agit des classes de données directement héritées du C# et régulièrement utilisées dans Unity. Vous les rencontrerez très souvent, car quel que soit le type de programme, elles permettent de faire des modélisations simples.

Les grands classiques : bool, int, float, string, char

  • Le booléen : il n’a que deux états possibles « True » et « False ». Pour les états binaires
  • L’entier : un chiffre entier, positif ou négatif. Pour tout ce que vous n’aurez pas à fractionner : l’identifiant d’un niveau, le nombre de gemmes récoltées…
  • Le float : un chiffre à virgule flottante (en utilisant le point comme séparateur décimal et en faisant suivre). Pour les pourcentages, le temps, la monnaie, tout ce qui nécessite de la précision.
  • La chaîne de caractère : pour toute donnée texte, typiquement ce qui est saisit par le joueur (nom d’un personnage, nom du vaisseau…), ou construit par votre programme (nom d’une sauvegarde), ainsi que le texte qui est affiché à l’écran.
  • Le caractère : définit par des guillemets simple, il contient un unique caractère (lettre, chiffre symbole, etc.) ou une série de caractère qui correspond à un seul caractère (\n pour un retour à la ligne). Pas si fréquent dans le gamedev, typiquement utilisé pour l’ASCII Art.
public bool Alive = true;   // booléen
public int Lives = 5;       // entier
public float Power = 15.63f;    // float (nombre à virgule flottante)
public string Level = "Sharp World"; // chaîne de caractère
public char Emblem = 'G';   // caractère seul

Les énumérations, un type « à la carte »

L’énumération est un type rencontré moins couramment dans les initiations au développement, mais incontournable, à mon sens, dans le cas du jeu vidéo.

On y stocke un nombre fini de possibilités (associées implicitement ou explicitement à des valeurs). Il peut vous permettre de mettre en place une machine à états, et de travailler plus facilement sur un set de valeur finies.

L’utilisation d’un enum se fait en deux parties : d’abord, il vous faut définir cet enum. Et ensuite, vous pourrez utiliser l’enum ainsi défini en tant que variable.

public enum Role { Tank, Healer, Fighter, Support };
public Role CharacterRole = Role.Fighter;

public enum WeaponState { Perfect, SlightlyDamaged, WornOut, Broken };
public WeaponState MainWeaponState = WeaponState.Perfect;
public WeaponState SecondaryWeaponState = WeaponState.WornOut;

Attention, l’endroit de définition de l’enum a son importance. Un enum défini hors d’une classe sera disponible dans toutes les classes, tandis qu’un enum défini au sein d’une classe ne le sera que s’il est public (et il faudra alors l’appeler avec le nom de la classe, aussi bien l’enum que ses valeurs !).

public class MetricsManager : MonoBehaviour
{
    private GameManager.Difficulty difficulty = GameManager.Difficulty.Easy;
    // Mon enum "Difficulty" est un enum public, défini dans la classe GameManager.
    // Je peux l'utiliser dans cette autre classe MetricsManager.
}

Dans Unity 3D, l’inspecteur adapte automatiquement la présentation au type de données disponible dans la classe. Un booléen sera une case à cocher, un champ chiffre ou texte sera un champ de saisie, et un enum sera présenté sous forme d’un menu de sélection.

Exemple de présentation des types C# de base dans l’inspecteur d’Unity 3D

Tableaux et collections

Enfin, vous disposez des collections : types List, Dictionnary, tableaux associatifs, etc. : à plusieurs reprises, vous aurez probablement besoin de stocker un tableau ou une liste d’objets. Voir l’article dédié sur les listes, dictionnaires et autres collections pour découvrir tout ce que vous pouvez faire avec celles-ci.

Vous pouvez faire des collections en utilisant les objets / types de votre choix, même ceux que vous créez vous-même.

Dans l’inspecteur, Unity présentera les collections sous forme d’une liste facile à manipuler (l’ajout, la suppression et le tri des données de la collection se fait directement à la souris) :

Exemple de collection (List<string>) dans l’inspecteur d’Unity 3D

Les « Types Unity » : des structures prêtes à l’emploi

Les classes ci-dessous sont des structures, qui existent dans des composants Unity et que vous pouvez utiliser dans vos propres composants et classes.

Pour utiliser ces structures, comme toutes les suivantes, vous aurez besoin d’appeler le namespace du moteur Unity en début de script. Ne vous inquiétez pas, Unity ajoute cette ligne par défaut.

using UnityEngine;

Les Vecteurs et Quaternions : informations dans un univers 3D ou 2D

Les structures de type Vector3, Vector2, etc. sont des vecteurs mathématiques, un ensemble de plusieurs float ou entiers.

On pourrait les écrire sous forme de tuple (float x, float y, float z) MyVector, mais l’avantage d’avoir une classe dédiée dans Unity permet de disposer d’office d’un ensemble d’outils, de manières de les initialiser et de travailler dessus logiquement, etc.

  • Vector3 : un vecteur en 3 dimensions, 3 float (x, y, z), idéal pour travailler dans un espace en 3D. La position, la rotation et l’échelle des éléments en 3D utilisent des Vector3, idem pour le moteur physique.
  • Vector2 : deux dimensions en float (x, y) Permet de travailler dans un plan 2D, donc utile pour tout ce qui est 2D, interface utilisateur, coordonnées sur une carte…
  • Vector4 : moins utilisé que les deux autres, il est surtout utilisé en interne, notamment pour certains shaders. 4 dimensions (x, y, z, w).
  • Quaternion : semblable au Vector4, c’est une manière pour Unity de représenter les rotations en interne sous forme de 4 dimensions. Les Quaternions demandent une connaissance avancée des mathématiques, et pour la plupart des gens il est plus simple d’utiliser les EulerAngles (représentation des rotations en Vector3).
[Header("Vector3")]
public Vector3 NextMissionDestinaton = Vector3.zero;            // Vecteur 3 (x, y, z)
public Vector3 MainBasePosition = new Vector3(24f, 10f, 60f);
[Header("Vector2")]
public Vector2 TwiceTwentyFive = Vector2.one*25f;          // Vecteur 2 (x, y)
public Vector2 MainMapSize = new Vector2(2000f, 800f);          
[Header("Vector4")]
public Vector4 PositionInTimeAndSpace = new Vector4(11f, 22f, 33f, 2021f);  // Vecteur 4 (x, y, z, w)
[Header("Quaternion")]
public Quaternion WorldRotation = new Quaternion(12f, 4f, 8f, 9f);      // Quaternion (x, y, z, w)

Autre avantage par rapport à un tuple : ces vecteurs sont tous nativement présentés dans l’inspecteur d’Unity.

Présentation des vecteurs (Vector3, Vector2, Vector4) dans l’inspecteur d’Unity 3D.

Couleurs : intégration poussée des valeurs RGBA

Color et Color32 sont des structures qui permettent de représenter des couleurs, avec un canal transparent. Le tout au format RGBA (Rouge, Vert, Bleu, Alpha/Transparence).

Color prend 4 valeurs float (de 0f à 1f) correspondant à des pourcentages pour chaque canal. Color32 prend les valeurs au format 32 bits, donc 4 entiers de 0 à 255). Vous pouvez transférer les valeurs d’une structure à l’autre.

Color est cependant à privilégier car le nombre de couleurs possible est plus élevé, et il est aussi plus simple pour nous de comprendre un codage en pourcentage de chaque canal que les valeurs allant de 0 à 255.

public Color ArmorColor = new Color(1f, 0.2f, 0.5f, 1f);    // Couleur définie en % de R, G, B, A
public Color EnemyArmorColor = Color.blue;  // Valeur de bleu prédéfinie dans la structure Color.
public Color32 ClanColor = new Color32(255, 130, 0, 200);  // Couleur définie en valeur 0-255 de R, G, B, A

L’inspecteur propose une bonne intégration pour ces couleurs, avec un sélecteur de couleurs complet (avec un système de pipette, et la possibilité de saisir les couleurs en 0-255, en 0f – 1.0f, ou même en hexadécimal).

Couleurs (Color et Color32) dans l’inspecteur d’Unity.
Sélecteur de couleur dans l’inspecteur Unity

Quelques autres structures

D’autres structures sont intégrées à Unity, pour des usages un peu plus spécifiques :

  • LayerMask : un sélecteur pour les Layers (les calques) d’Unity, tels que définis dans Project Settings > Tags & Layers. Permet de sélectionner un, plusieurs, tous ou aucun des calques.
  • Rect : définition d’un rectangle (coordonnées X, Y et dimensions W et H – weight/height).
  • AnimationCurve : permet de définir des images clefs, façons courbe de Beziers. Typiquement utilisé pour de l’animation, car ils permettent de définir un état qui change au fur et à mesure du temps.
  • Gradient : définition de dégradés de couleurs, utilisés par exemple pour les particules. Ici aussi, Unity a une interface très complète pour définir les dégradés.
[Header("Autres structures")]
public LayerMask HiddenInLayers;    // Type LayerMask : choix multiple parmi une liste de calques (Layers)
public Rect FlagShape = new Rect(5f, 10f, 200f, 600f);   // Définition d'un rectangle (position X et Y, puis dimensions W et H)
public AnimationCurve AnimationCurve; // Courbe d'images-clé
public Gradient BannerGradient;   // Dégradé

Ils disposent tous d’une présentation dédiée dans l’inspecteur d’Unity :

Autres structures dans Unity : LayerMask, Rect, AnimationCurve, Gradient

Des classes pour référencer des objets et assets de votre projet

Deux objets fondamentales : Scene et GameObject

Scene : les scènes sont le fichier de base d’Unity (extension en .unity). Typiquement, c’est un espace dans lequel tous les éléments sont placés.

Il est nécessaire d’utiliser l’espace de nom dédié pour avoir les scènes.

using UnityEngine.SceneManagement;

Par défaut, il n’y a pas d’intégration des champs « Scene » dans l’inspecteur (vous verrez une variable sans champ). Les scènes sont gérées par la classe SceneManager et des appels via des noms ou numéros de scène.

GameObject : objet de base dans Unity. Concrètement, tout élément de la scène est un GameObjet composé d’un ou plusieurs composants. Un cube, par exemple, est un GameObjet avec un composant pour gérer le rendu,

Avec un champ GameObject en public (ou en SerializeField), vous pouvez directement faire référence à un objet A dans un objet B, récupérer ses infos et agir dessus. L’objet peut être aussi bien un objet présent dans la scène qu’un GameObject de type Prefab présent dans les Assets.

[SerializeField] private GameObject mainGameObject;

void Start()
{
    if (mainGameObject.TryGetComponent(out GameManager gameManager))
    {
        gameManager.DoSomething();
        gameManager.RecordTransform(this.transform);
    }
}

Le GameObject est une classe tellement centrale dans Unity qu’il fera l’objet d’un article dédié.

Référence à un GameObject dans l’inspecteur

Images, sons et matériaux

  • Les Sprite sont des objets graphiques en 2D, qui servent typiquement aux jeux en 2D. Unity dispose d’outils pour créer des sprites et les animer depuis une image contenant plusieurs sprites (qu’on appelle un Atlas de sprite). Un sprite est affiché à l’écran par un composant Image.
  • Les AudioClip sont des containers pour des données audio. Typiquement, lorsque vous importez un son dans le répertoire de votre application, Unity va l’importer en tant qu’AudioClip, et vous pourrez alors le jouer via un composant AudioSource. La classe AudioClip contient des infos vous permettant de savoir si le son est chargé, quelle est sa fréquence, etc.
  • Les Material stockent une instance d’un shader, avec des réglages spécifiques. Un même shader peut être utilisé pour présenter l’apparence de différents matériaux, et concrètement, ça sera un Material qui permettront de présenter ce shader de manière à ce qu’il ressemble soit à du métal, soit à du bois, soit à du plastique…
Sprite, AudioClip et Materials dans Unity 3D

Pour aller plus loin…

Nous avons abordé ici de nombreux éléments proposés par Unity, mais gardez à l’esprit que ce n’est qu’un nombre d’éléments limités. Je me suis volontairement limité à tout ce dont on peut typiquement avoir besoin dans les premières heures de développement de son jeu, mais il est possible d’aller beaucoup plus loin.

Toutes les classes d’Unity peuvent être adressable, et l’inspecteur d’Unity est capable par défaut de rendre ces classes sous forme de champs avec contraintre. Par exemple, si vous ajouter un champ « Collider » à votre script, l’inspecteur vous présentera un champ spécifique. Si vous faites glisser un GameObject dans ce champ, il n’acceptera la manœuvre que si le GameObject dispose d’un composant héritant de Collider, et le champ s’adressera uniquement à ce composant spécifique.

Ici, on a fait glisser notre GameObjet « Cube » dans un champ de type « Collider » : ce qui est assigné à ce champ, c’est uniquement le « Box Collider » du GameObject « Cube ».

Enfin, nous avons évoqué de nombreux champ parfaitement intégrés à l’inspecteur d’Unity, mais celui-ci est entièrement modélisable selon vos besoins. Vous pourrez créer des inspecteurs spécifiques pour présenter des classes que vous aurez créé. Un des principes de l’inspecteur étant typiquement de séparer la couche Game Design / réglages de la couche développement et code.