# TP1 : Fondamentaux Unity
Rémy Frenoy, Florian Jeanne, Yann Soullard, Azzeddine Benabbou, Yohan Bouvet (maj 2022)
Unity est un logiciel et un moteur de jeu multi-plateforme (PC, Mac, Linux, mobiles, tablettes, consoles de jeux et applications web). Il est principalement utilisé dans le domaine des jeux vidéo, mais également en réalité virtuelle.
## Création d’un projet
Lors du lancement de Unity, ce n’est pas le logiciel qui s’ouvre, mais le « Unity Hub », qui sert à gérer vos projets ainsi que les différentes versions d’Unity installées.

Figure : L'interface de Unity Hub peut être différente si vous avez une version plus récente.
Créez un nouveau projet en sélectionnant le template « 3D URP » et nommez-le comme vous le souhaitez. Il est possible que le template ne soit pas téléchargé. Il faudra alors le télécharger.
:::info
La création d'un projet prend généralement du temps. C'est donc normal si vous voyez des fichiers se charger en continu.
:::
## Interface graphique de Unity

Figure : Interface principale de Unity pour l’édition d’un projet
1 (rouge) **Hierarchy** – Hiérarchie des éléments disposés sur la scène.
2 (vert) **Project** – Ensemble des ressources du projet.
2 bis (vert) **Console** – Sortie des erreurs de compilation en mode Scene, et les outputs console en mode Game (ex: messages de Debug.Log(« message »))
3 (bleu) **Scene** – Scène d’édition, permet d’ajouter, modifier, supprimer des éléments de l’environnement.
3 bis (bleu) **Game** – Affiche l’application une fois celle-ci lancée.
4 (orange) **Inspector** – Affiche les propriétés et les options de configuration de l’objet sélectionné.
5 (violet) **Control bar** – Permet de lancer, suspendre et arrêter l’application en cours.
## Sauvegarder la scène
Lors de la création d’un nouveau projet, Unity crée une première scène, qui est déjà enregistrée dans les ressources (_Assets_), dans le dossier _Scenes_. En ouvrant le dossier, vous pouvez observer que le nom de l’objet correspond à celui qui est tout en haut de la hiérarchie. Pour signifier que ceci va être notre scène principale, faites un clic-droit sur l’objet _SampleScene_ et renommez-la en _Main_. Selon la version de Unity, il est possible que l'éditeur vous indique que ce changement nécessite de recharger la scène ! Après recharge, vous pouvez voir que l’objet, dans la fenêtre _Hierarchy_, a été renommé.
(Pour les versions antérieures à 2021 : le dossier « Assets > Scenes » n’est pas automatiquement créé et votre scène pas sauvegardée, vous devez le faire manuellement !)
La quantité de ressources présentes dans un projet Unity peut rapidement être imposante.
Ces ressources sont très variées (scènes, scripts, matériaux, …). Nous nous attacherons donc à organiser rigoureusement nos ressources dans des dossiers correspondants à leur nature (les scènes dans un dossier “Scènes”, les scripts dans le dossier “Scripts”, etc). Toutes les ressources Unity sont considérées comme des “assets”, ainsi tous les dossiers que vous créerez seront à placer dans le dossier “Assets”.

Figure : L'organisation de vos projets devrait ressembler plus ou moins à ça.
Avant d'attaquer la suite de ce TP, vous pouvez surpprimer l'objet _Global Volume_ présent dans la scène et le dossier *TutorialInfo*
## Naviguer dans la scène
Pour naviguer dans la scène, Unity Editor propose plusieurs façon de faire. A vous de vous habituer et adopter les habitudes de navigation qui vous conviennent.
- Placez votre curseur sur la scène et déplacez vous grâce aux **touches directionnelles**.
- Sélectionnez l'outil _View Tool_ (cf.image). Maintenez le **bouton gauche** de la souris appuyé puis bougez votre souris. Vous pouvez utiliser les touches directionnelles pour vous déplacer.

Figure: Outil _View Tool_
- Pour vous déplacer dans la scène comme vous le feriez dans un jeu à la première personne (Fly mode), maintenez le **bouton droit** de la souris appuyé puis bougez votre souris. Utilisez ensuite les boutons **Q,S,D,Z** et **A,E**.
:::info
Par défaut, Unity Editor est configuré pour une utilisation avec un clavier QWERTY. Pour modifier cela, il suffit d'aller dans **Edit > Shortcuts**, puis sélectionnez la catégorie **3D Viewport**.
:::
## Créer un objet
Nous sommes maintenant familiarisés avec l’interface. Cependant, la scène est vide… Ou presque ! Comme nous pouvons le voir dans la fenêtre _Hierarchy_, il y a deux éléments :
_Main Camera_ et _Directional Light_, qui sont une caméra par défaut et une lumière d’ambiance.
Nous allons commencer par créer des objets simples, comme une balle et un sol par exemple (dans Unity, les objets sont appelés _Game Objects_). Pour cela, il vous suffit d’aller dans le menu puis dans **GameObject > 3D Object > Plane**, pour le sol. L’objet apparaît dans la scène et dans la hiérarchie des objets. Il est déjà sélectionné et prêt à être renommé, comme ce sera le sol, renommons-le en “Ground” dans la fenêtre _Hierarchy._ (Si vous avez cliqué ailleurs et que l’objet n’est plus sélectionné, vous pouvez toujours faire un clic droit sur l’objet “Plane”, puis _Rename_).
Faites la même chose pour la balle que l’on nommera “Ball” : **GameObject > 3D Object > Sphere**.
## Positionner un objet
Pour positionner les objets dans la scène, il faut tout d'abord sélectionner l'outil _Move Tool_. 
Lorsque vous sélectionner ensuite l'objet à déplacer, vous devriez voir apparaitre 3 flèches sur l'objet sélectionné. Ces flèches correspondent aux 3 axes : x,y et z.

Figure : Les 3 axes affichés sur la sphère (x en rouge, z en bleu et y en vert).
:::info
Dans Unity, l’axe par défaut pour la hauteur est l’axe Y. Si vous avez du mal à vous souvenir quel axe représente la hauteur, une astuce : les axes X, Y, Z sont représentés par les couleurs R, V, B. L’axe Y est vert : **vert** comme **vert**ical !
:::
Pour déplacer la sphère, sélectionnez un axe et maintenez le bouton gauche de la souris appuyé puis déplacez la sphère.
Dans certains cas, on aimerait définir précisement la position de l'objet dans la scène grâce à des valeurs numériques. Pour cela, dans la fenêtre _Inspector_, localisez la partie _Transform_. C’est ici que nous allons pouvoir modifier la position, la taille et l’orientation des objets. Tous les _GameObjects_ possèdent un _component Transform_.
- Placez la Sphère dans la position (0,3,0).
- Placez le Plan à la position (0,0,0)

Figure : Notre balle est au-dessus du sol.
- Lancez la scène grâce au bouton Play. Il ne se passe rien. C'est normal !
## Composants et physique
Maintenant que notre scène possède des objets, il serait intéressant de pouvoir les déplacer.
Pour cela, nous allons devoir ajouter une nouvelle propriété à notre balle. Dans le jargon Unity, on parlera de **Composant** (_Component_) afin de lui ajouter des contraintes physiques. Sélectionnez donc la balle puis ajoutez-lui un _rigidbody_ : dans la fenêtre _Inspector_, cliquez sur le bouton _Add Component_, ou dans le menu **Component > Physics > Rigidbody**. Un nouveau _component_ est alors ajouté à notre balle dans l’_Inspector_.
Lancez la scène grâce au bouton _Play_. Miracle, la gravité est rétablie et la balle tombe !
## Déplacer un objet
### Programmer les interactions (un peu de lecture)
Les interactions consistent à lier une commande, par exemple l’appui sur une touche, avec un comportement. Lorsque vous faites un clic droit avec la souris, vous **interagissez** avec l’application. Pour notre balle, c’est la même chose : on veut pouvoir la déplacer, par exemple grâce aux flèches directionnelles du clavier. Depuis la version de Unity 2019, un nouveau système de gestion des commandes (**_Inputs_**) a été développé. Pour comprendre la différence, il faut comprendre comment se fonctionne l’application.
Lorsque vous cliquez sur **Play** pour lancer la scène (et voir votre balle tomber), des scripts se lancent. Parmi eux, certains seront créés par nous, pour pouvoir « faire ce qu’on veut ». Le comportement qui permet de dire qu’un GameObject qui possède une propriété RigidBody est automatique : la présence de la propriété RigidBody fait que l’objet sera considéré comme ayant un corps soumis aux lois physiques. Pourquoi peut-on voir l’objet tomber, pour finalement s’arrêter sur le sol ? Car il existe des fonctions qui tournent en boucle, et qui permettent d’actualiser l’état du monde, qui sera ensuite affiché. Un peu comme au cinéma, où un film n’est qu’une succession de photos.
Lorsqu’on clique sur **Play**, tous les objets de la scène lancent un script nommé « Start », qui permet de les initialiser dans le monde. Par exemple, on aurait pu laisser notre balle coincée dans le sol et se servir du script « Start » pour changer sa position initiale.
Une fois cela fait, à chaque « photo » du monde (**_frame_**), tous les objets lancent leur fonction **Update**. C’est là qu’on va pouvoir calculer les changements qui peuvent avoir lieu, par exemple, si on a demandé à la balle de se déplacer, c’est ici qu’on va calculer de combien elle doit se déplacer en fonction du temps écoulé et de la durée d’appui sur une touche.
Avant la version 2019 de Unity, on devait donc tester nous-mêmes, dans un script qu’on aurait créé, dans cette fonction **Update**, si, à chaque frame, il y a eu un appui sur une touche, et si c’est le cas, faire les changements appropriés.

Figure : Gestion des inputs dans les versions de Unity inférieures à 2019
Comme on ne peut pas changer le code d’un script « pendant » que l’application est lancée, si on veut finalement utiliser une autre touche, il faut avoir pensé à tester tous les cas possibles dans notre fonction **Update**. Depuis la version 2019, ce n’est plus le cas, et un nouveau système a été développé. À présent, lors de la fonction Update, on va juste tester si la commande liée à l’action qu’on veut faire a été activée ou non.

Figure : Gestion des inputs dans les versions de Unity depuis la version 2019
### Input Manager (Old)
Dans le cadre de ce TP, nous utiliserons en premier lieu l'ancien Input Manager pour s'abstraire de la complexité de mise en place du nouveau système. Nous basculerons vers ce nouveau système plus tard.
Pour déplacer notre sphère, il nous faudra créer un script C#. Mais avant cela, et dans un souci d'organisation, nous allons créer un dossier Scripts dans notre projet où seront stockés tous nos scripts (si vous ne l’aviez pas déjà fait !). Pour cela, faites un clic droit dans la fenêtre Project > Create > Folder. Maintenant, faites un clic droit dans le dossier nouvellement créé puis Create > C# Script (ou allez dans Assets > Create > C# Script à partir du menu). Un fichier apparaît dans l’explorateur du projet. Renommez-le “BallController”. Ce script va nous servir à contrôler le déplacement de notre balle. Il faut donc l’associer au GameObject “Ball”. Un simple glisser-déposer de l’objet BallController sur la balle, que ce soit dans la scène ou dans la hiérarchie, suffit. Vous pouvez également le faire depuis l’Inspector avec le bouton Add Component (Scripts > Ball Controller). Le script apparaît maintenant comme component.
Pour éditer notre script, double-cliquez sur lui et l'étideur que vous avez lié à Unity (Visual Studio Code dans notre cas) s’ouvrira alors automatiquement. Le script comprend par défaut deux méthodes : Start() et Update(). Start() contient les instructions qui seront exécutées au chargement du script, à sa première exécution. Elle n’est donc appelée qu’une seule fois ; Update() est appelée à chaque frame, et contient donc les instructions qui seront exécutées en boucle.
Créez un script *BallController* et placez le dans un dossier _Scripts_. Sélectionnez ensuite votre sphère, puis dans l'inspector à droite cliquez sur _Add Component_ tout en bas. Puis, Dans notre script *BallController*, définissons une nouvelle méthode nommée, par exemple, KeyboardMovements(). Cette méthode aura pour objectif de tester les touches du clavier qui sont utilisées et d’appliquer une force sur notre balle dans la direction souhaitée. Nous aurons besoin de récupérer la référence au « corps » de l’objet, le _Rigidbody_. Nous avons les éléments pour définir KeyboardMovements() qui sera appelée dans notre **_Update_**.
Comme indiqué dans la documentation Unity, le paramètre de la fonction ``AddForce()`` est un ``Vector3``. Il existe des Vector3 de base qui sont préétablis, comme ``Vector3.left`` qui correspond au vecteur ``(-1, 0, 0)``. En fonction de la touche appuyée, nous appliquerons donc une force dans la direction du Vector3 spécifié.
Récapitulons :
- Nous avons besoin de récupérer une référence au composant Rigidbody de note balle (voir méthode [GetComponent](https://docs.unity3d.com/ScriptReference/GameObject.GetComponent.html)). C'est sur ce composant que nous allons agir.
- Nous avons besoin de savoir si l'utilisateur a appuyé sur une des touches prévues pour déplacer la balle (voir les méthodes de la classes [Input](https://docs.unity3d.com/ScriptReference/Input.html)). On utilisera pour cette exemple, les touches directionnelles du clavier.
- Si l'utiliseur a appuyé sur une touche prévue pour déplacer la balle, nous devons appliquer une force dans la direction souhaitée (voir la méthode [AddForce](https://docs.unity3d.com/ScriptReference/Rigidbody.AddForce.html) et la classe [Vector3](https://docs.unity3d.com/ScriptReference/Vector3.html))
:::spoiler Indice
```csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BallController : MonoBehaviour
{
private Rigidbody rb;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
//D'abord, on teste et applique les déplacements
KeyboardMovements();
//Par la suite, on peut mettre à jour d'autres choses...
}
void KeyboardMovements()
{
//Si on utilise des else if, on autorise l'appui sur une seule touche à la fois
//Si on veut pouvoir aller en diagonale, il faudrait faire le AddForce à la fin et gérer l'ajustement des valeurs dans les if, sans qu'il y ait de else
if(Input.GetKey( ... ))
{
....
}
else if(Input.GetKey( ... ))
{
}
else if(Input.GetKey( ... ))
{
}
else if(Input.GetKey( ... ))
{
}
}
}
```
<!--
```csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BallController : MonoBehaviour
{
private Rigidbody rb;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
//D'abord, on teste et applique les déplacements
KeyboardMovements();
//Par la suite, on peut mettre à jour d'autres choses...
}
<!--
void KeyboardMovements()
{
//Si on utilise des else if, on autorise l'appui sur une seule touche à la fois
//Si on veut pouvoir aller en diagonale, il faudrait faire le AddForce à la fin et gérer l'ajustement des valeurs dans les if, sans qu'il y ait de else
if(Input.GetKey("right"))
{
rb.AddForce(Vector3.right * speed);
}
else if(Input.GetKey("left"))
{
rb.AddForce(Vector3.left * speed);
}
else if(Input.GetKey("up"))
{
rb.AddForce(Vector3.forward * speed);
}
else if(Input.GetKey("down"))
{
rb.AddForce(Vector3.back * speed);
}
}
}
```
-->
:::
<br>
Sauvegardez le script et retournez sur l’interface de Unity. S’il y a des erreurs, celles-ci apparaîtront dans l’onglet **Console**, juste à côté de l’onglet **Projet**.
Appuyez sur play et amusez-vous en utilisant les touches. Attention à ne pas dépasser le sol !
Ajouter ensuite une variable publique ```speed``` et modifier votre script de façon à ce que la balle se déplace à la vitesse définie dans la direction souhaitée.
## Placer la caméra
Unity place automatiquement une caméra sur la scène. Vous pouvez sélectionner cette caméra en choisissant _Main Camera_ dans la fenêtre _Hierarchy_. Un pop-up _Camera Preview_ s’ouvre et affiche la vue depuis la caméra sélectionnée. Vous remarquerez, en scrutant l’_Inspector_, que la caméra est positionnée en (0, 1, -10). Déplacez la caméra de telle sorte que vous puissiez voir le plan et la sphère dans _Camera Preview_.
Lancez l’application. Déplacez la sphère afin qu’elle sorte du champ de la caméra. On ne la voit plus (c’était le but de l’opération…). Une question existentielle se pose : comment faire pour que la caméra suive la sphère ?
### Mieux placer la caméra

La hiérarchie des objets permet de définir un objet comme étant enfant d’un autre. Glissez-déposez l’objet _Main Camera_ sur _Ball_ dans la fenêtre _Hierarchy_ : la caméra est désormais “enfant” de la sphère.
Lorsqu’un objet est enfant d’un objet _parent_, sa position devient relative à la position du parent, c’est-à-dire que le référentiel de l’objet enfant n’est plus le centre de la scène (0,0,0) mais le centre de son objet “parent”. Relancez l’application. La position initiale de la caméra ne change pas. Déplacez la sphère. La caméra se déplace désormais en suivant les déplacements de la balle. Ça vous plaît ? J’espère que vous avez le cœur bien accroché…
La méthode consistant à placer la caméra comme étant l’enfant d’un objet que l’on souhaite suivre est pertinente dans le cas d’un personnage à la première/troisième personne par exemple. En vue à la première personne, disposer la caméra comme “enfant” permet par exemple de suivre les déplacements et les mouvements de la tête. Attention toutefois aux déplacements de l’objet “parent” ! Lorsque celui-ci est amené à faire de nombreuses rotations (typiquement le cas d’une sphère !), mieux vaut utiliser une autre méthode…
### Correctement placer la caméra
On souhaite que la caméra suive les translations de la sphère dans l’espace, mais pas les rotations. La caméra ne peut donc pas avoir comme référentiel celui de la balle. Dans la hiérarchie, faites donc en sorte que la caméra ne soit plus enfant de la sphère.

Figure : S’émanciper dans Unity, c’est facile!
On se retrouve dans la configuration initiale, avec une caméra fixe. Pour modifier cela, ajoutons un script _CameraController_ à nos assets, et affectons-le à l’objet _Main Camera_. Ouvrez le script dans MonoDevelop. Dans ce script, on souhaite que tout changement de position (c’est à dire une translation dans l’espace) d’un GameObject modifie de la même manière la position de la caméra. Nous avons donc besoin de deux variables :
- Une variable que l’on nommera _baseObject_, de type GameObject et qui correspondra à l’objet que l’on souhaite suivre.
- Une variable que l’on nommera _offset_, de type Vector3 et qui correspond au vecteur représentant l’écart entre la position initiale de la caméra et la position initiale de l’objet à suivre.
L’esprit de ce script est donc de repositionner la caméra afin que l’offset soit constant.
Il suffit donc de calculer l’offset au chargement de la scène (donc dans la méthode Start) de la façon suivante :
```
offset = transform.position - baseObject.transform.position;
```
A chaque frame (donc dans la méthode Update), on modifie la position de la caméra comme étant la position de l’objet à suivre plus l’offset.
```
transform.position = baseObject.transform.position + offset;
```
On obtient alors le code suivant :
```csharp!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraController : MonoBehaviour
{
public GameObject baseObject; //l'objet à suivre
private Vector3 offset; //la distance initiale entre l'objet et la caméra
// On calcule l'offset avant même que la balle ne tombe!
void Start()
{
offset = transform.position - baseObject.transform.position;
}
void Update()
{
transform.position = baseObject.transform.position + offset;
}
}
```
Lancez l’application.

Figure : Une erreur de compilation sauvage apparaît!
Oups ! En effet, nous avons déclaré avoir besoin d’une variable baseObject dans le script, mais Unity n’a aucune idée de quel objet il s’agit. C’est donc à nous de lui donner cette information.
Sélectionnez la caméra dans la fenêtre _Hierarchy_ afin que ses propriétés apparaissent dans la fenêtre _Inspector_. Dans le cadre Camera Controller, vous voyez votre script, et vous voyez également la variable Base Object qui est pour l’instant à None, d’où l’erreur de compilation !
Depuis la fenêtre _Hierarchy_, glissez-déposez la balle vers la valeur de BaseObject dans la fenêtre _Inspector_. Vous obtenez ceci :

Lancez l’application, et déplacez la balle. Vous remarquez que la caméra suit les déplacements de la balle, sans en subir les rotations. Parfait !
## Ajouter des textures aux objets
Cette scène est un peu terne. Ajoutons-y un peu de couleur. Pour modifier l’apparence d’un objet, celui-ci doit avoir un _material_. Commençons par créer un dossier “Materials” dans la fenêtre _Project,_ qui permettra de stocker l’ensemble des _materials_ utilisés dans un projet. Pour créer un _material_, faites un **clic droit > Create > Material**. Renommez-le “GreenColor”. Pour donner une couleur verte à votre _material_, modifier la propriété _Base Map_ dans la fenêtre _Inspector_ et attribuez-lui la couleur verte. Pour appliquer un _material_ sur un objet, il suffit de glisser-déposer le _material_ depuis la fenêtre _Project_ vers l’objet dans la scène.

Figure : Résultat de l'application du _Material_ sur la balle.
Il est également possible d’utiliser des images plutôt que de simples couleurs. Créez un dossier **_Images_** dans le dossier **_Assets_**. Copiez-y l'image de gravier que vous pouvez télécharger [ici](https://hackmd.io/_uploads/B1rcOvHKT.png) (ou une autre image de votre choix). Nommez-la _gravels_, par exemple.
Créez maintenant un nouveau _Material_ “Gravels”. Sélectionnez ce nouveau _material_ afin qu’il s’affiche dans la fenêtre _Inspector._ Cliquez sur le petit cercle à gauche de la propriété **_Base Map_** et choisissez l’image _gravels_ dans la fenêtre qui apparaît.
Comme nous l’avons fait précédemment pour la sphère, glissez-déposez le nouveau _Material_ sur le sol de la scène.
## Exporter votre projet
Notre projet terminé, nous souhaitons l’exporter (si votre TP n’est pas terminé, exportez-le quand même, c’est facile mais important). Pour cela, dans le menu tout en haut, faites **Assets > Export Package**.
Vérifiez bien que tout est coché, puis confirmez. Vous obtenez un fichier portant l’extension _unitypackage_. Vous pouvez désormais importer dans n’importe quel projet les assets (dont la scène) que vous venez d’exporter.
## Passage au nouveau système d'Input (Input System Package)

Nous allons ici vous présenter le [nouveau système d'input](https://docs.unity3d.com/Packages/com.unity.inputsystem@1.8/manual/index.html). Pour changer la façon dont les commandes sont gérées, allez dans le menu Edit > Project Settings > Player et chercher dans la partie « Configuration ». Si la propriété « Active Input Handling » est à « Input Manager (Old) », cela veut dire que Unity s’attend à ce que les inputs soient gérés dans la boucle Update. Cliquez sur la propriété et changez-la par « Input System Package (New) ». Unity va vous prévenir qu’il doit se relancer pour prendre en compte le changement. Acceptez. Une fois Unity relancé, vous pouvez fermer la fenêtre **Project Settings**.
Allez dans le menu Window > Package Manager, cliquez sur le bouton « Packages : In My Project » pour sélectionner « Packages : Unity Registry », et chercher le package Input System. Cliquez dessus et installez-le.

Figure : Ça paraît lourd, mais ça vous aidera pour développer sans avoir besoin de casque!
Cela va nous permettre de créer un nouvel asset : dans la fenêtre **_Project_**, faites un clic droit et **Create > Input Actions**. Ce nouvel asset va vous servir à lier des actions (dont l’activation sera testée dans les fonctions Update) et des commandes. Cliquez sur l’objet, puis dans l’_Inspector_, **Open**.
Une nouvelle interface s’est ouverte, nous allons la détailler.

Figure : Nous avons renommé notre asset « AllControls », d’où le nom affiché en haut.
1 (rouge) **Control Scheme** : la configuration matérielle qu’on va utiliser (par exemple, quand vous jouez à un jeu vidéo et que vous avez la possibilité d’alterner entre souris/clavier et manette…)
2 (bleu) **Action Maps** : des groupes d’actions qu’on va développer (pensez aux jeux vidéo, quand vous vous déplacez à pied puis en voiture…)
3 (vert) **Actions** : les actions qui seront testées, et les commandes qui les déclenchent.
4 (orange) **Properties** : les propriétés des commandes
Avant tout chose, cochez la boîte « Auto-save », sinon il vous faudra cliquer manuellement sur le bouton « Save Asset » pour que vos changements soient pris en compte…

Vide, l’interface n’est pas très parlante, alors commençons à remplir pour comprendre. Tout d’abord, nous allons créer un **Control Scheme** : cliquez sur « No Control Scheme » puis sélectionnez « Add Control Scheme ». Nommez le schéma « MainControls », puis cliquez sur le petit « + » sous la liste vide : c’est là où nous allons préciser quel matériel est nécessaire (ou optionnel) pour commander notre balle. Restons simples et sélectionnons « Keyboard ». Cliquez sur « Save » : ça y est, on a défini que pour pouvoir interagir, nous aurons besoin d’un clavier.
Dans **Action Maps**, comme notre application est simple, cliquez sur le « + » et nommez le groupe « Main ». Une nouvelle action s’est automatiquement créée ! Cliquez sur le petit triangle à gauche pour l’ouvrir.

Pour l’instant, notre action se nomme « New action » et n’est liée à aucune commande. Comme nous voulons déplacer la balle, renommons l’action en « MoveBall » (double-clic ou clic droit sur l’action). Dans les propriétés, nous pouvons voir que l’action a un type, ici « Button ». Il existe en fait 3 types d’actions, mais nous en utiliserons principalement 2 :
- Button : l’action va être liée à un seul input binaire (l’appui sur la touche Espace permet de sauter)
- Value : l’action va être liée à une commande sur la durée (tant qu’on appuie sur la flèche droite, on se déplace à droite)
Comme nous gérons ici le mouvement, le type de l’action va être « Value ». Tant qu’on appuiera sur une touche, la balle se déplacera dans un sens. Mais le fait d’avoir une « valeur » renvoyée va nous permettre de pouvoir appuyer sur plusieurs touches en même temps et d’aller dans la bonne direction (si on veut aller en diagonale par exemple) car le calcul sera fait automatiquement !
Dans Control Type, sélectionner « Vector 2 » pour indiquer que nous souhaitons un control sur 2 vecteurs. Il est à noter que ce sélecteur conditionne les choix possibles du cadre « Action ».
Définissons maintenant quelles touches nous allons utiliser : faites clic-droit sur l’action **MoveBall**, nous voulons que la balle se déplace selon 2 axes (X et Z), donc ajoutons un «2D Vector Composite» (ou **Up\Down\Left\Right Composite**). (Le « No Binding » est inutile, vous pouvez le supprimer en faisant clic droit dessus et _Delete_)

Figure : Après l'ajout du 2D Vector Composite
Nous avons presque fini la création de nos inputs. Cliquez sur une direction, et dans les propriétés, cliquez sur « Path » : la liste des matériels disponibles (quand vous avez créé le Control Scheme) s’affiche. Naviguez dans Keyboard pour trouver la touche que vous voulez utiliser, ou bien dans la barre de recherche.

:::warning
La partie « Usages » propose les touches les plus couramment utilisées dans les applications pour l’interaction, mais ne sont pas forcément en accord avec le matériel que vous avez choisi. Mieux vaut ne pas l’utiliser. Il y a également la solution de facilité, qui consiste à cliquer sur le bouton « Listen » puis à appuyer sur la touche (ou le bouton d’une manette, ou un clic de souris) désirée, ce qui va réduire les choix.
:::

Figure : En cliquant sur *Listen* puis sur la flèche du bas, l'interface me propose la touche (Down Arrow), mais également « n’importe quelle touche ». Comme je veux utiliser la flèche vers le bas, je sélectionne la première proposition.
Maintenant que j’ai affectée une touche à chacune des directions, l'interface devrait ressembler à la capture suivante :

## Ça roule !
Maintenant, nous allons mettre en place le script pour déplacer la balle.

Sélectionnez la balle. Pour qu’elle puisse adapter sa position, il faut qu’elle puisse savoir si des inputs ont été activés. Pour cela, dans la fenêtre _Inspector_, ajoutez un nouveau _Component > Input > Player Input_. Il faut maintenant lui préciser quelles sont les commandes et les actions qui lui seront appliquées : pour cela, faites un glisser-déposer de l’asset InputActions que vous venez de créer dans le champ « Actions ».
L’objet Ball est maintenant au courant des actions qui peuvent être activées. Il ne nous reste plus qu’à lui dire quoi faire lorsque c’est le cas.
Avec le nouveau système de gestion des inputs, l’appui sur les touches que nous avons liées à notre action **MoveBall** permet de lancer un signal à notre objet Balle. Chaque action entraîne la création d’une méthode dont le nom est structuré de la même façon : **On[Le_Nom_De_Votre_Action]**. Ici, notre action s’appelle **MoveBall**, nous aurons donc une fonction dans notre script qui s’appellera **OnMoveBall**. Si on avait créé une action ChangeColor, la fonction qu’on devrait créer s’appellerait OnChangeColor, etc.
Le script agit comme un _component_ de la balle, il a donc accès aux autres composants de celle-ci ; c’est pour cela que nous le lions à la balle.
```csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class BallController : MonoBehaviour
{
private Rigidbody rb;
private Vector2 moveValue;
// Start : Fonction appelée une seule fois au lancement du script
void Start()
{
rb = GetComponent<Rigidbody>();
}
// Update : Fonction appelée à chaque frame
void Update(){
rb.AddForce(moveValue.x, 0, moveValue.y);
}
// OnMoveBall : Fonction appelé à chaque fois qu'une touche liée à l'action MoveBall a été appuyée
void OnMoveBall(InputValue value){
Debug.Log("Move Ball Action is called");
moveValue = value.Get<Vector2>();
Debug.Log(moveValue);
}
}
```
``Debug.Log(moveValue)`` affiche à chaque fois la valeur du vecteur qui est reçu par la fonction. Appuyez sur une touche, puis deux touches à la fois et observez les valeurs.
## Pour aller plus loin
### Les lumières
Comme son nom l’indique, la lumière placée automatiquement lors de la création d’une scène est directionnelle. Vous pourrez trouver de nombreux autres types de lumière (Point Light, Spot Light, Area Light) dans GameObject >Light. Vous pouvez également modifier les propriétés de ces lumières dans la fenêtre _Inspector_, une fois la lumière sélectionnée.
### L’Asset Store
Unity possède un asset store qui permet de télécharger des contenus gratuits ou payants afin de les intégrer à un projet.
Les contenus sont classés par catégories (3D Models, Textures and Materials, …).
Parmi les contenus, vous trouverez notamment des fichiers portant les extensions .**fbx** et **.obj**. FBX et OBJ font partie des principaux formats de fichiers de modèles 3D. Des logiciels de modélisation tels que Blender ou 3ds Max permettent de créer de tels fichiers.
## Solution du TP
- Téléchargez et importez le package suivant : [Package TP01](http://azed.in/courses/virtual_reality/tp/solutions/TP01.unitypackage)