NavMesh
Triggers et klaxon
En ajoutant un collider sur le prefab de votre NavMeshCar, par exemple un rectangle devant la voiture, en sélectionnant l’option isTrigger et en surchargeant la méthode OnTriggerEnter(), vous pouvez faire en sorte que les voitures klaxonnent lorsqu’elles se rapprochent dangereusement d’un objet. Pour jouer un son, il est possible d’utiliser la fonction AudioSource.Play(). Vous pouvez créer une source audio depuis un objet AudioClip, comme celui-ci pour le klaxon.
Note : les conducteur⋅ice⋅s ne klaxonnent usuellement pas quand ils/elles voient le sol ; utilisez un tag pour les en empêcher.
C’est l’heure du jeu. Nous avons une véritable autoroute. Cependant, c’est un peu vide comme application. Nous allons donc créer un petit jeu. Ajoutez un FirstPersonCharacter comme dans le TD précédent, et positionnez-le sur le bord de la route (pensez à configurer correctement votre (New)Input System pour interagir avec votre personnage!). Le but du jeu sera de traverser les voies sans se faire renverser par une voiture. A chaque fois que le personnage traverse la route sans encombre, il gagne un point. Cependant, s’il se fait toucher par une voiture, son score est décrémenté de un point et il retourne à sa position initiale.
Pour gérer le score, nous allons créer un Game Manager qui stockera le score et les méthodes servant à l’incrémenter ou le décrémenter. Ce script peut être par exemple ajouter à un GameObject vide quelconque que l’on nommera “GM”.
Ensuite, positionnez un trigger de chaque côté de la route, de manière à ce que le personnage soit détecté une fois qu’il arrive de l’autre côté. On pourra par exemple créer un cube faisant toute la largeur du plan (voir image) et désactiver son Mesh Renderer afin de n’avoir que son Collider (dont le isTrigger est positionné à True).
Associez-leur un script qui incrémente le score une fois que le joueur passe d’un côté de la route à l’autre. Cependant, pour ne pas incrémenter le score plus d’une fois à chaque fois, on désactivera le trigger dans lequel le joueur vient de pénétrer, et on activera celui de l’autre côté de la route, ainsi de suite. Le premier trigger, c’est-à-dire celui dans lequel se trouve le personnage au départ, est de base désactivé. Faites néanmoins attention. On ne désactive pas le GameObject contenant le trigger, mais bien le composant en lui-même à l’aide de Collider.enabled.
GUI : Graphical User Interface
Votre jeu marche bien, le score fonctionne correctement mais nous ne l’affichons pas encore au joueur. C’est donc le moment de voir comment l’interface graphique est gérée sous Unity. Il existe différents éléments existants sous Unity pour composer votre GUI, vous pouvez retrouver différents tutoriaux à cette adresse : Doc Unity. Aujourd’hui nous allons seulement nous intéresser à trois types d’éléments de l’interface à savoir les boutons, le texte et les images.
Tout d’abord, il faut savoir que n’importe quel composant de l’interface graphique, donc un bouton ou autre, sera toujours intégré dans un Canvas. Ce Canvas est une zone délimitant notre interface. Il sera généré automatiquement lorsque vous créer un bouton par exemple. Le component Rect Transform d’un canvas n’est pas modifiable, il s’adapte à le fenêtre Game (ou à l’écran si lorsque le jeu compilé est en plein écran).
Dans notre application, nous désirons afficher le score du joueur. Ajoutez donc un texte à votre scène ( GameObject > UI > Text – TextMeshPro ). La première fois, Unity vous proposera d’installer TMP Essentials pour que TextMeshPro fonctionne correctement, ainsi que des exemples. Cliquez sur Import TMP Essentials. Comme tous les éléments d’interface d’Unity, le Gameobject Text possède un Rect Transform qui vous permet de le positionner dans le Canvas et de définir manuellement sa taille. On y retrouve aussi les notions d’ancre ou encore de pivot qui vous permettent de placer votre éléments par rapport à l’objet parent, si et seulement si celui-ci possède également un Rect Transform, ce qui est le cas dans 99.999999% des cas. On veut afficher le score en haut à gauche et ce, quelque soit la taille de l’écran. Nous allons donc figer le texte en haut à gauche de notre Canvas à l’aide de l’ancre correspondante.
L’objet Text créé comprend un composant Text (UI), qui lui-même contient un champ texte. Cependant, ce champ est utile si notre texte était statique. Or, nous voulons actualiser le score en temps réel, il faut donc que le texte soit dynamique. Pour cela nous allons créer un script. Ajoutez donc un script au Gameobject Text. Il devra récupérer le score stocké dans notre GameManager (via une variable public par exemple) et l’afficher en modifiant le texte de l’objet Text (vous suivez toujours ?). Cependant, pour pouvoir accéder à la propriété Text.text, nous devons inclure la librairie correspondant à l’interface utilisateur. Ajoutez donc tout en haut de votre script la ligne :
using TMPro;
Une fois ceci terminé, votre score doit se mettre correctement à jour et s’afficher dans la foulée.
Chargement et lancement du jeu
Créez une nouvelle scène et ajoutez lui une image (GameObject > UI > Image). Modifiez sa taille de façon à ce qu’elle remplisse le Canvas. Vous pouvez faire cela directement dans le Rect Transform, au niveau des ancres prédéfinies en maintenant la touche Alt enfoncée. Vous pouvez changer la couleur de fond avec celle que vous préférez. Si vous le souhaitez, vous pouvez ajouter une image à votre projet, et comme pour les cursors du TD précédent, vous devez changer la nature de l’image dans Unity. Sélectionner votre image dans la fenêtre projet et changer le paramètre Texture Type à la valeur Sprite (2d and UI).
Ajoutez ensuite un texte et deux boutons à votre canvas comme ceci :
Les couleurs des boutons et du texte sont à mettre suivant goûts personnels ! Nous devons cependant associer des actions aux boutons. Notre bouton Play devra charger la scène principale contenant notre jeu, alors que Quit fermera l’application.
Créez tout d’abord un script que nous appellerons AppManager, et contenant deux méthodes publiques :
Quit()
qui servira à fermer l’application à l’aide de EditorApplication.isPlaying si vous êtes dans l’éditeur ou Application.Quit()
dans un build ; Play()
qui chargera donc le jeu à l’aide de SceneManager.LoadScene()
dans l’éditeur ou Application.LoadLevel()
pour un build. Vous pouvez ajouter ce script au canvas par exemple, ce n’est pas très important ici. Ensuite, nous devons ajouter nos deux scènes créées dans les paramètres du projets afin qu’elles soient prises en compte pendant le build du projet. Pour cela, allez dans File > Build Settings. En ayant au préalable sauvegardé votre scène, cliquez sur le bouton “Add Current”. Chargez votre scène principale et refaites la même opération. Vos scènes sont alors toutes les deux ajoutées au build du projet.
Ensuite, il ne vous reste plus qu’à relier le script aux boutons. Sélectionnez un des boutons, dans l’inspector vous verez un petit tableau On Click(), cliquez sur le + puis glissez le canvas depuis la hiérarchie jusqu’à la case none (object) (c’est comme nos variable public), ensuite vous aurez accès à tous les éléments, components, scripts, class etc. pour déclenchez une action On click(). Vous l’aurez compris, nous allons choisir la fonction AppManager.Quit()
sur le bouton du même nom, et la fonction AppManager.Play()
sur le bouton du même nom. Vous devez obtenir ceci :
Pour info :
Dans le script que nous venons de créer, Application.Quit()
ne fonctionnera que si vous effectuez un build du projet, c’est à dire si vous générez un exécutable indépendant de Unity. Ainsi, si vous lancez l’application comme d’habitude dans l’éditeur, cette méthode de fonctionnera pas (comme précisé dans la doc Unity de la méthode Quit()
) ;
Pour la méthode LoadLevel()
, vous pouvez mettre en paramètre le nom de votre scène principale ou son identifiant. Pour connaître ce dernier, il suffit d’aller dans les paramètres du build comme expliqué précédemment et de relever le numéro associer à la scène correspondante. Vous remarquerez que vous pouvez ajouter autant d’appels que vous le souhaitez dans On Click(), et qu’il y a un sélecteur pour définir si l’action est exécutée lorsque l’on est dans l’éditeur, la Runtime (la build) ou les deux. Vous pouvez donc avoir une méthode pour la version éditeur, et une méthode pour la version build.
Pour aller plus loin Notre NavMesh est très rudimentaire (pour un cas aussi simple, il est même possible de faire sans, mais c’était l’occasion de vous familiariser avec cet outil qui peut s’avérer très utile et puissant). Vous pouvez l’améliorer de nombreuses façons, par exemple en faisant une route plus complexe (vous pouvez utiliser l’asset EasyRoads3D Free), ou en considérant le personnage comme un obstacle mobile, les voitures essayant alors de l’éviter plutôt que d’aller tout droit.