Blog ENI : Toute la veille numérique !
-25€ dès 75€ sur les livres en ligne, vidéos... avec le code FUSEE25. J'en profite !
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. Unity3D
  3. Le système de scripting
Extrait - Unity3D Développer en C# des applications 2D/3D multiplateformes (iOS, Android, Windows...)
Extraits du livre
Unity3D Développer en C# des applications 2D/3D multiplateformes (iOS, Android, Windows...)
1 avis
Revenir à la page d'achat du livre

Le système de scripting

Les scripts, des composants à part

1. Définition

Nous avons vu dans le chapitre précédent qu’Unity repose sur le principe de la programmation par composition. Les scripts sont les composants permettant au développeur de créer un comportement spécifique pour un GameObject.

Il est possible de créer et d’attacher autant de scripts que nécessaire sur un GameObject. Il ne faut ainsi pas avoir peur de découper vos comportements en plusieurs petits scripts chacun dédié à une tâche bien précise.

Un script Unity peut être écrit en C#, ou en UnityScript. Dans le premier cas, il dérivera alors de la classe MonoBehaviour dérivant elle-même de la classe Component et n’aura pas d’héritage dans le second cas. Ce livre ne traitera que des scripts réalisés en C# mais les principes de base restent les mêmes.

Lors de la création d’un script, essayez de le garder générique au maximum afin de pouvoir le réutiliser sur un autre GameObject.

2. Ajouter un script à un GameObject

L’ajout d’un script personnalisé est tout aussi aisé que l’ajout d’un composant. L’éditeur offre la possibilité de créer notamment des scripts en C# ou en UnityScript.

 Dans la fenêtre Hierarchy, sélectionnez Create...

Comment afficher des informations de débogage

Il est primordial au cours des phases de développements d’un logiciel de pouvoir avoir des informations pendant l’exécution de celui-ci. Unity nous propose plusieurs solutions passant toutes par des méthodes statiques de la classe UnityEngine.Debug.

1. Écrire dans la console

Il est possible d’écrire directement dans la vue console intégrée à l’éditeur d’Unity. Pour cela, il faut choisir la méthode correspondant au niveau de criticité que l’on veut donner au message à écrire :

  • Log ou LogFormat pour les messages informatifs,

  • LogWarning pour les messages importants,

  • LogError pour les erreurs,

  • LogException pour afficher spécifiquement une exception.

Il est à noter que pour chacune de ces méthodes, il est possible de fournir une instance de GameObject. Cela aura pour effet de le mettre en surbrillance dans la hiérarchie lorsque le message sera sélectionné dans la console.

Voici un exemple de code mettant cela en exergue :


Debug.Log("[Log] Je suis un log", gameObject); 
Debug.LogFormat("[Log] formatage : {0:F3}", 42); 
Debug.LogError("[Error] Je suis une erreur"); 
Debug.LogWarning("[Warning] Je suis un warning"); 
Debug.LogException(new ArgumentException("Message de l'exception"));...

Le cycle de vie des scripts

Un script possède son propre cycle de vie lié à la fois au composant sur lequel il est placé et à l’application en elle-même. Ce cycle de vie est matérialisé par la présence de fonctions définies dans la classe C# du script.

Fait assez déroutant au début pour un développeur rompu à la programmation objet, les méthodes correspondantes ne sont pas présentes sur la classe de base et il vous faudra les définir vous-même sans les surcharger (override).

Dans tous les cas, les différentes méthodes du cycle de vie ne seront exécutées par Unity uniquement si le GameObject sur lequel est attaché le script est activé.

Aussi, il est important de bien respecter les majuscules et minuscules définissant les méthodes, sous peine qu’elles soient ignorées par Unity.

1. Initialisation du script

Trois méthodes principales existent pour initialiser un script : Awake, Start et OnEnable. Ces méthodes seront les premières exécutées dans la vie d’un script.

a. La méthode Awake

La méthode Awake est appelée une seule fois dès que votre script est présent sur un GameObject de la scène : que le composant "script" soit activé ou non (le GameObject doit cependant être bien activé). Il s’agit donc de l’endroit privilégié pour initialiser des éléments liés à ce script en question ne dépendant pas d’autres scripts ou GameObjects.

Dans cet exemple de code, le texte "AffichageDuCycleDeVie --> Awake" est affiché dans la console lorsque la méthode Awake est appelée par Unity.


private void Awake() 
{ 
    Debug.Log("AffichageDuCycleDeVie --> Awake"); 
}
 

Une fois le programme exécuté, la console est bien remplie malgré la désactivation du script AffichageDuCycleDeVie.

images/05_03_01.PNG

Vous remarquerez que la méthode n’est pas une surcharge, que sa signature déclare qu’elle ne retourne rien (void) et que son niveau d’accessibilité est private. Dans les exemples suivants, le niveau d’accessibilité ne sera plus nécessairement présent car cette...

Comment les scripts interagissent avec l’extérieur

1. Permettre la configuration d’un script

Un script a souvent besoin d’être configuré pour répondre au contexte où il est placé. Cela est notamment très pratique lorsque vous mettez ces scripts à disposition d’autres développeurs ou intégrateurs.

Unity propose une solution simple à ce problème : tous les champs publics seront visibles dans l’Inspector avec l’éditeur de valeur le mieux approprié.

Ainsi, si l’on ajoute un champ de type boolean IsVeryImportant à un script Interactions, il sera visible dans l’éditeur.


public class Interactions : MonoBehaviour 
{ 
    public bool IsVeryImportant; 
}
 
images/05_04_01.PNG

2. Accéder au GameObject parent

L’élément le plus important pour un script est sans doute le GameObject parent auquel il est attaché. Il est très simple d’y accéder car il est exposé par la propriété gameObject de la classe MonoBehaviour dont héritent tous les scripts.

Il est alors possible de lire les propriétés de base de celui-ci comme le nom ou le tag :


var nomDuGO = gameObject.name; 
var tagDuGO = gameObject.tag;
 

3. Manipuler les composants de base

a. Trouver un composant déjà ajouté

Pour trouver un composant déjà présent sur un GameObject il suffit d’utiliser la méthode GetComponent. Celle-ci retourne null si le composant n’existe pas sur ce GameObject.


var camera = gameObject.GetComponent<Camera>(); 
bool cameraExist = camera != null;
 

Il est important ici de noter que la propriété gameObject expose elle-même plusieurs composants "classiques" sous la forme de propriétés : camera, collider, light, etc. Ces méthodes bien pratiques permettent d’accéder aux composants les plus communs sans recourir à la lourdeur de GetComponent. Cependant elles sont depuis Unity 5 marquées comme obsolètes et pourraient disparaître dans une version ultérieure d’Unity.

b. Ajouter un composant

À partir d’un script, il est possible d’ajouter au GameObject parent un nouveau composant directement au niveau du code en utilisant la méthode...

Écriture de code pour une seule plateforme

L’objectif annoncé d’Unity est d’écrire un même code pour toutes les plateformes disponibles. Il peut cependant être intéressant de vouloir profiter dans certains cas des spécificités de telle ou telle plateforme.

1. Utiliser les propriétés de la classe Application

La méthode la plus naturelle pour un développeur va sans doute être d’utiliser les propriétés statiques de la classe Application :

  • Application.isEditor : retourne true si le code est exécuté depuis l’éditeur.

  • Application.platform retourne la plateforme actuellement utilisée.

Voici un exemple testant si le code est exécuté dans l’éditeur, sur la PS4 ou sur une autre plateforme :


if (Application.isEditor) 
{ 
    Debug.Log("Non disponible dans l'éditeur"); 
} 
else if (Application.platform == RuntimePlatform.PS4) 
{ 
    Debug.Log("Non disponible sur PS4"); 
} 
else 
{ 
    // Autre code 
}
 

Attention, plusieurs valeurs de l’énumération RuntimePlatform sont marquées comme obsolètes.

2. Utiliser les directives de précompilation

Une autre méthode pour mettre en place du code pour une seule plateforme est d’utiliser...

Exécuter des actions simultanément avec les coroutines

1. Définition

Une méthode est, par défaut, exécutée entièrement durant une même frame dans Unity. En caricaturant le processus, Unity parcourt pour chaque frame les scripts qu’il doit considérer, les exécute, et une fois cela terminé, il passe à la frame suivante. La durée d’une frame est donc entièrement déduite du temps d’exécution des scripts.

Une coroutine consiste en une méthode dont l’exécution est potentiellement faite sur plusieurs frames successives ou non.

2. Pourquoi les coroutines sont nécessaires

Prenons pour exemple un script devant rapprocher la caméra d’un objet petit à petit pour illustrer la problématique. Pour cela, nous allons manipuler la caméra et la déplacer de 100 pixels dans une boucle for avec ce code :


public class CoroutinesBasics : MonoBehaviour 
{ 
    public Camera TargetCamera; 
    private bool _launched = false; 
 
    void Update() 
    { 
        // Si le déplacement n'est pas déjà lancé 
        if (!_launched) 
        { 
            _launched = true; 
 
            // Déplacement de la caméra de 100 pixels 
            for (int i = 0; i < 100; i++) 
            { 
                TargetCamera.transform.Translate(0, 0, 1); 
            } 
        } 
    } 
}
 

Ce code n’aura pas du tout l’effet attendu ! En effet, comme...

Quelques design patterns utiles

1. Singleton

a. Définition

Le singleton est un design pattern classique du développement logiciel qui ne sera pas décrit in extenso dans ce livre. L’idée de base consiste à avoir une instance unique d’un objet afin de lui laisser la responsabilité d’orchestrer différentes actions, de gérer la durée de vie d’objets sous sa responsabilité ou encore d’être un référent unique accessible facilement par les autres composants techniques de la solution.

Le développement Unity ne fait pas exception et c’est un composant vite incontournable. 

b. Implémentation du singleton dans Unity

L’implémentation technique du singleton peut être améliorée en faisant dériver la classe finale de MonoBehaviour afin de pouvoir bénéficier de tous les événements liés à son cycle de vie et y réagir. Pour que ceux-ci soient réellement effectifs, il faut bien veiller à attacher ce script à un GameObject. L’astuce pour réunir ces deux états de fait consiste à créer un GameObject à la volée que l’on placera dans la scène si le script n’en a pas encore.

La première étape consiste à créer une classe de base pour nos futurs singletons se chargeant de cette logique primaire. La méthode FindObjectOfType d’Unity permet de récupérer une instance (la première qu’il trouve) d’un objet du type fourni dans la scène s’il existe (ou de retourner null le cas échéant). Cette méthode trouve toute son utilité dans ce cas :


public class BaseSingletonUnity<T>  
    : MonoBehaviour where T : MonoBehaviour 
{ 
    protected static T _instance; 
 
    public static T Instance 
    { 
        get 
        { 
            if (_instance == null) 
            { 
                // récupération d'une instance existante du script 
            ...