---
# System prepended metadata

title: TP03 Unity

---

Rémy Frenoy, Yann Soullard, Florian Jeanne,
Luca Pelissero-Witoslawski, Olivia Reaney & Baptiste Wojtkowski,
Yohan Bouvet, Azzeddine Benabbou

## Retour sur les TP1 et TP2

Lors des deux premières séances de TD, nous avons appris à créer différents types d’objets dans Unity, à interagir avec pour les déplacer de différentes manières (_AddForce_, _Translate_). Nous en connaissons un petit peu plus sur la gestion de la physique (_rigidbody, colliders, triggers_). Nous connaissons plusieurs fonctions de mise à jour de l’affichage des scènes (Update, FixedUpdate, LateUpdate). Nous avons également travaillé sur le positionnement de la caméra (fixe, héritage d’un objet, suivi des translations via un script dédié).

## Starter Assets

Unity dispose de packages “starter” correspondant à des éléments de base utilisés dans la majorité des projets. C’est de cette façon que nous avons utilisé le nouveau système de gestion des inputs. Si ces packages sont très utiles, ils doivent être utilisés à bon escient car il est important de connaître et maîtriser le fonctionnement global des éléments que l’on importe dans un projet. Nous allons aujourd’hui utiliser le package « Starter Assets – First Person Character Controller». Créez un nouveau projet **3D URP**. Comme je le disais, le bon escient dans ce cas est l’option URP du projet puisque le starter asset fonctionne en URP (Universal Render Pipeline).  
Auparavant, les packages standard étaient directement accessibles depuis Unity, ce n’est plus le cas depuis la version 2019. On va donc devoir passer par l’Asset Store qui, depuis la version 2021, n’est plus accessible non plus directement depuis Unity…

![image](https://hackmd.io/_uploads/rybmklwKT.png)
Figure : Ce meme illustre bien les changements de version de Unity.

Dans votre navigateur préféré, allez sur le Unity Asset Store (ou cliquez [ici](https://assetstore.unity.com/) pour plus d’efficacité). Beaucoup d’assets sont payants, mais certains, notamment ceux permettant d’apprendre à utiliser Unity, sont gratuits (sinon à quoi bon aller plus loin…). Allons donc dans le menu  « Essentials » puis « By Unity » pour voir ce qu’ils nous proposent. Dans la liste d’assets, trouvez « Starter Assets – First Person Character Controller » . Cliquez dessus : la description nous indique que cet asset est destiné à la version 2020.3 et plus, et qu’il nécessite l’URP. Un autre asset porte presque le même nom : « starter assets – Third Person Character Controller », ne vous trompez pas. Cet asset sert, comme son nom l’indique, à faire une application en vue à la 3ème personne.


Si vous avez « acheté » (oui, même quand c’est gratuit, vous avez acheté…) via votre compte, vous pouvez retrouver votre package dans le menu « Window > Package Manager ». Cliquez sur le « Packages : … » (comme on faisait pour l’Input System) puis sélectionner « Packages : My Assets » : tadam ! Tous vos assets achetés se retrouvent ici ! Sélectionnez le « Starter Assets… » et cliquez sur « Download », puis sur import.

![image](https://hackmd.io/_uploads/SJVE1xwYp.png)


Ce package, comme beaucoup d’autre, a des dépendances. Il vous demandera si vous souhaitez les installer automatiquement ou si vous souhaitez le faire vous même plus tard. Cliquez sur « Install/Upgrade ». Unity va installé 2 dépendances (3 si votre projet n’est pas créé en URP) :

- Premièrement, il utilise le New Input System, donc configurez votre projet comme nous l’avions vu au premier TP (il est possible que Unity le fasse autoamatiquement pour vous, vérifiez).
- Deuxième dépendance : « Cinemachine », c’est un package de gestion de caméras dynamiques. Vous pouvez le trouver lui-aussi dans le « Package Manager » à la section « All packages ».
- (troisièmement : le package Universal RP, non on n’y coupera pas !)

Une fois les dépendances installées, un nouveau message apparait :

« Voulez-vous redémarrer pour prendre en compte le New Input System ? »

Deux options possibles :

- Répondez « oui », unity redémarre, mais l’import est incomplet. Refaite-le, mais en cliquant sur « skip » à l’étape des dépendances. Une fois sur la fenêtre vous montrant l’ensemble des fichiers du package, cliquez sur « All » puis sur « Import ».
- Répondez « non », la fenêtre vous montrant l’ensemble des fichiers du package apparait directement, cliquez sur « All » puis sur « Import ». Ensuite, éditez les propriétés du projet pour activer l’utilisation du New Input System. (menu « Edit »-> »Project Setting », onglet « Player » -> « Other Settings »,  « Active Input Handling » = Input System Package (New) ou  Both). Unity redémarrera pour prendre le changement en compte.

Vous pouvez voir que dans la fenêtre _Project_, dans vos Assets, se trouve un nouveau dossier du nom du package : c’est ici qu’on va trouver les fichiers que l’on va utiliser. Il existe même un fichier _Readme.asset_ qui vous fait un récapitulatif rapide de l’asset dans la fenêtre _Inspector_.

Si vous lisez attentivement, vous pouvez voir qu’il existe déjà des scènes préconçues pour tester ce que fait l’Asset. Mais on est là pour apprendre donc autant mettre les mains dans le cambouis ! Vous serez libres de tester plus tard si vous voulez…

## First Person Controller (FPC)

Nous voilà donc avec… rien du tout, car notre scène est vide (hormis la lumière et la caméra…).

![image](https://hackmd.io/_uploads/HywHJevtp.png)
 Nous allons utiliser l’asset _FirstPersonController_, qui correspond à un personnage à la première personne. Dans le dossier _StarterAssets_, vous trouverez un fichier PDF de documentation qui vous indiquera comment utiliser l’asset (une bonne habitude à prendre pour les projets…). Dans le dossier _StarterAssets/ FirstPersonController/Prefabs_ vous avez plusieurs _prefabs_ à votre disposition, dont deux « caméras ».Glissez l’objet **PlayerCapsule** sur la scène pour qu’on puisse l’analyser.

Notre objet **PlayerCapsule** possède une propriété _CharacterController,_ 3 scripts et un Player Input. Pour les plus observateurs, on peut remarquer que le vert de l’icône du _CharacterController_ ressemble beaucoup à celui des _Fixed_ et _Hinge Joint_ du TD2 et des _Rigidbody_… Eh oui, cette propriété signifie bien que nous avons affaire à un objet qui sera soumis aux lois de la physique ! D’ailleurs, vous pouvez également le déduire grâce au nom du 2ème script _BasicRigidBodyPush_, si ça veut pas dire ce que ça veut dire…

Notre petite capsule a donc un corps qui pourra entrer en collision avec son environnement. Super ! Créons-lui donc un sol pour ne pas qu’il tombe à l’infini ! Ajoutez un plan dans votre scène, en vous assurant que votre capsule (votre personnage) soit bien au-dessus…

![image](https://hackmd.io/_uploads/HyvLJlDt6.png)

Pourquoi utilise-t-on un [_CharacterController_](https://docs.unity3d.com/Manual/class-CharacterController.html) et pas un [_Rigidbody_](https://docs.unity3d.com/Manual/class-Rigidbody.html) un peu customisé, comme on a pu le faire dans le TP2 ? Souvenez-vous : lorsque votre balle rentrait en contact avec un objet, vous aviez un petit effet rebond, c’est normal, c’est de la physique… Dès qu’il y a collision entre des colliders, les forces entrent en jeu. Mais imaginez un personnage qui marche, et dont le petit orteil rencontre un caillou : le fait d’avoir un _rigidbody_ ferait que votre personnage recule, quand bien même il aurait simplement pu marcher dessus… On a donc besoin d’un _rigidbody_ un peu plus permissif lorsqu’on se déplace, et là vient le _CharacterController !_

Mais alors, si c’est la même chose, qu’est-ce qui change ? Un component _rigidbody_ possède une propriété [isKinematic](http://docs.unity3d.com/ScriptReference/Rigidbody-isKinematic.html), qui, lorsqu’elle est activée (la checkbox est cochée), empêchent les forces de s’appliquer à l’objet (mais on pourra toujours tester les collisions !) Notre propriété _CharacterController_ est un _Rigidbody_ caché, dont la propriété isKinematic est active. Mais si les forces ne s’appliquent pas, comment on va rétablir la gravité ? Le script _FirstPersonController_ s’en occupe ! Et il nous permet de modifier toutes les valeurs qui permettront à notre personnage de se déplacer (vitesse de marche, de course, hauteur des sauts, etc.)

![image](https://hackmd.io/_uploads/H1dY1evtp.png)


Figure : D’ailleurs, on peut même changer la gravité grâce aux variables publiques… Un petit tour sur la Lune ?

![image](https://hackmd.io/_uploads/Sk-5kewY6.png)


Ok, on a donc un sol et un corps. Mais comment va-t-on faire pour se déplacer ? Faisons un peu de retro-engineering pour deviner… Dans le dossier **InputSystem**, ouvrez l’objet **_StarterAssets.inputactions_**.

![image](https://hackmd.io/_uploads/HyJo1xPFp.png)


Figure : Notre personnage peut donc se déplacer, regarder, sauter et sprinter.

Laissons de côté le saut et le sprint.

![image](https://hackmd.io/_uploads/HkUj1gvYa.png)
Figure : On peut assigner autant de touches que l’on souhaite à une même commande.

Pour se déplacer, nous pouvons donc utiliser les touches ZQSD ou les flèches directionnelles. Pour regarder, on utiliser le curseur, donc la souris, et surtout le delta, c’est-à-dire ses déplacements.

Lancez l’application : vous observez le vide, le sol à vos pieds, peut-être même la capsule en fonction de la position de votre caméra. Déplacez-vous… Tiens ? La capsule bouge, mais on est loin de la vue à la première personne ! Il doit nous manquer quelque chose… 

### Configuration de la caméra du FPC
Revenez dans les **Assets >** **StarterAssets > FirstPersonController > Prefabs**. 
- Supprimez la _MainCamera_ de la scène et glissez celle des prefabs. Il y a un petit dessin dans la _Hierarchy_ à côté de la _MainCamera_, ça indique l’utilisation de l’asset CinemachineBrain (il s’agit d’un gestionnaire de caméras dynamique mais ce n’est pas le sujet du TP).
-  Glissez le prefab « PlayerFollowCamera » sur la scène, puis dans l’inspector à l’option « Follow » glissez-y le GameObject « PlayerCameraRoot » (c’est un enfant de « PlayerCapsule »).

![image](https://hackmd.io/_uploads/Byrn1gwYT.png)


La caméra se positionne automatiquement à la bonne position. On vérifie cela en lançant l’application : vous pouvez vous déplacer sur le plan grâce aux flèches directionnelles et bouger la tête grâce à la souris (comme un personnage à la première personne en fait…).

:::info
Dans le script FirstPersonController, c’est la méthode CameraRotation qui permet de déplacer la caméra lorsque la souris bouge. Elle fait également en sorte que les déplacements de la caméra ne soient pas trop rapides pour une impression visuelle plus agréable (vomir en jouant, c’est pas cool !). Cela vaut aussi pour le mouvement : dans la méthode Move, des interpolations linéaires sont faites grâce à la fonction_ [_Lerp_](https://docs.unity3d.com/ScriptReference/Mathf.Lerp.html)_, et on_ [_Clamp_](https://docs.unity3d.com/ScriptReference/Mathf.Clamp.html) les valeurs de déplacement de la souris pour éviter que notre tête ne tourne sur 360° (verticalement), ou on serait obligés d’appeler un exorciste…
:::

## Le Ray Casting
### Le principe
On souhaite désormais pouvoir saisir des objets dans l’environnement virtuel. On va pour cela utiliser le ray casting. Le principe du ray casting est de créer un rayon, depuis une position d’origine et dans une direction particulière. Ce rayon va nous indiquer s’il est entré en collision avec un objet ou non. Pour davantage d’information sur le ray casting, c’est [ici](http://docs.unity3d.com/ScriptReference/Physics.Raycast.html). ![image](https://hackmd.io/_uploads/HJmTkgDYa.png)
Figure : Raycasting

Le principe du ray casting est simple : la fonction emet un rayon et renvoie ``True`` si le rayon a détecté un objet, sinon elle renvoie ``False``. Les informations concernant l’objet détecté (s’il y en a un) se retrouvent dans la variable ``hitInfo`` passée en paramètre de la fonction.

### Création du rayon et affichage
Nous allons utiliser le ray casting pour cibler les objets que l’on pourra déplacer. Pour cela, créez un script _RayCasting._

On souhaite que le rayon parte de la caméra, dans la direction de la position du curseur de la souris dans l’environnement. Vous trouverez des informations utiles pour générer ce type particulier de ray casting [ici](http://docs.unity3d.com/Manual/CameraRays.html).

:::spoiler Indice pour ceux qui souhaitent tricher un peu
La fonction ``ScreenPointToRay`` d'un objet ``Camera`` permet de renvoyer un rayon allant de la caméra à travers un point de l'écran donné en paramètre.
Attention : *Cette fonction renvoie un rayon mais ne l'émet pas. L'emission du rayon se fait par ``Raycast``*
:::
<br>

Le rayon ira de la caméra à travers un point de l'écran. Ce point correspond à la position du curseur de la souris. Comment récupérer les coordonnées de ce point ? 
Même si nos inputs sont gérés par l’objet InputActions, on peut quand même récupérer « manuellement » des données, la position du curseur par exemple. On va s’affranchir de tous les tests pour vérifier qu’on a bien une souris connectée à l’ordinateur et considérer que oui. [Mouse.current](https://docs.unity3d.com/Packages/com.unity.inputsystem@1.8/manual/Mouse.html) nous permet d’accéder à la souris branchée, on peut donc récupérer la position du curseur.

:::spoiler Indice : si après 5 minutes vous n'arrivez pas à créer un rayon
```csharp
 ray = mainCamera.ScreenPointToRay(Mouse.current.position.ReadValue());
```
:::
<br>

Maintenant que le rayon est créé, ce serait bien de pouvoir l'afficher. Pour débugger et voir où se trouve votre rayon, vous pouvez utiliser la fonction  [Debug.DrawRay](https://docs.unity3d.com/ScriptReference/Debug.DrawRay.html) (ou [Debug.DrawLine](https://docs.unity3d.com/ScriptReference/Debug.DrawLine.html)). Bien évidément, les arguments de ces fonctions sont renseignés à partir du rayon que vous avez créé. Vous pouvez également choisir une couleur pour votre rayon.

:::warning
Attention ! Ces fonctions n’affichent le rayon **que** dans la fenêtre _Scene_, pas dans la fenêtre _Game_ ! Vous pouvez glisser la fenêtre _Game_ à côté de la fenêtre _Scene_ ou changer le layout Unity vers _2 by 3_ pour voir les deux fenêtres simultanément.
:::

![image](https://hackmd.io/_uploads/ryB7zI6Y6.png)
Figure : Rayon visible dans la vue _Scene_ 


### Caster un rayon
Tester si un objet est dans le rayon, ça ne fait pas tout : comment sait-on quel est cet objet ? Si vous regardez la documentation de la fonction [Physics.Raycast()](https://docs.unity3d.com/ScriptReference/Physics.Raycast.html), vous noterez qu’on a 4 définitions différentes de la fonction. C’est ce qu’on appelle la surcharge d’une fonction : pour faire court, la fonction renvoie toujours la même chose (est-ce que j’ai rencontré un objet sur le trajet du rayon) mais les paramètres qu’on lui passe sont différents. 

:::info
Notez dans la deuxième et la quatrième définition de la fonction ``Raycast()``, un paramètre nommé ``RaycastHit hitInfo``. Il a un mot clé devant : **"out"**. En C#, cela signifie que c'est un paramètre "de sortie". En d'autres termes, la fonction pourra modifier sa valeur. En quelque sorte, cela représente un moyen supplémentaire de renvoyer des valeurs (en plus du return). 
:::

- Castez votre rayon avec cette fonction et affichez, par exemple, le nom de l'objet avec lequel le rayon est entré en collision.



## Saisi d'un objet


### Ajout de l'input de la souris
Le rayon suivant désormais les mouvements de la souris, attaquons-nous à la saisie d’objets. 
<!--
Nous vous proposons l’algorithme suivant :

Lorsque l’utilisateur clique :

Si le raycast indique une collision avec un objet alors

Saisir l’objet

Sinon si l’utilisateur lâche le bouton de la souris et qu’un objet est saisi alors

Relâcher l’objet

Tant que l’objet est saisi alors

Déplacer l’objet à la position du rayon
-->

![image](https://hackmd.io/_uploads/SkbxggDYa.png)


Il va nous falloir un nouvel input : le clic souris. 
- Ouvrez l’objet InputActions (Assets/StarterAssets/InputSystem/StarterAssets.InputActions) et créez une nouvelle action Grab.
-  Attribuez-lui le clic gauche. Cochez _KeyboardMouse_ pour l’attribuer à la configuration clavier + souris (ce fichier est fait pour plusieurs configurations ou _Control Schemes_ vous pouvez les voir en cliquant sur _All Control Schemes_ en haut à gauche de la fenêtre). 

Dorénavant, lorsqu’on clique avec le bouton gauche de la souris, la méthode OnGrab sera appelée. Cependant, on ne veut pas seulement cliquer et attraper l’objet, on veut pouvoir le maintenir dans la main : on veut non seulement faire quelque chose quand on clique, mais également quand on relâche. Pour cela, on va attribuer une interaction particulière à notre action.

Cliquez sur le petit « + » à côté d’**Interactions** et sélectionnez « _Press_ ». Il existe un « _Hold_ » qui est trompeur : ce n’est pas « tant que l’on maintient appuyé… », mais un « déclencher après avoir tenu un temps T » !

Les propriétés _Interactions_ et _Processors_ servent à raffiner la façon dont les commandes seront appelées. Ici, lorsqu’on précise que la _Trigger Behavior_ est « Press and Release », cela signifie que la fonction OnGrab() sera appelée quand on enfonce le bouton (l’équivalent de « key down » pour les développeurs qui ont l’habitude de voir ce genre de script) mais aussi lorsque le bouton est relâché (l’équivalent de « key up »). Entre ces deux moments, la fonction ne sera pas appelée (normal : il n’y a pas de changement à signaler !). Laissons la valeur du « press point » à celle entrée par défaut (à partir de quand on considère que le bouton a été enclenché/relâché).

- Testez que votre fonction OnGrab est bien appelée.

A ce stade : 
- Vous avez crée un rayon.
- Vous arrivez à caster ce rayon.
- Vous arrivez à récupérer les informations sur les objets touchés par ce rayon.
- Vous avez une action qui est appelée lorsque le joueur clique sur le bouton gauche de la souris. 

C’est bien beau tout ça, mais on n’a rien à saisir… Créons donc une balle (avec un Rigidbody) et ajoutons-là à la scène !


### Saisir la balle
   
Voici une proposition d'algorithme 

    Lorsque l’utilisateur clique (bouton gauche) :
            Si (le raycast indique une collision avec un objet) alors
                Saisir l’objet
            Sinon si (l’utilisateur lâche le bouton de la souris et qu’un objet est saisi) alors
                Relâcher l’objet
            Fin si

    Tant que (l’objet est saisi) alors
        Déplacer l’objet à la position du rayon
    Fin Tant que



Maintenant qu’on sait comment saisir un objet, nous voulons le déplacer lorsque le clic est maintenu. En clair : on veut que la position de l’objet saisi soit mise à jour en suivant le regard (le rayon). Mais quand on l’a saisi, il était à une certaine distance de nous. Pour cela, il nous suffit de le positionner (via la fonction [MovePosition](http://docs.unity3d.com/ScriptReference/Rigidbody.MovePosition.html) par exemple) aux coordonnées suivantes :

```csharp
ray.origin + (ray.direction * offset)
```

L’_offset_ représente la distance **initiale** entre le personnage et l’objet saisi.

:::warning
Cette solution ne permet pas de rapprocher ou d’éloigner l’objet une fois saisi. La distance entre le personnage et l’objet est constante. L’objet se déplace donc dans une sphère de rayon offset dans le référentiel du personnage. Pour les plus aventureux, libre à vous après le TP de modifier ça et d’implémenter l’interaction via la molette de la souris par exemple !
:::



#### Problème 1 : La gravité sur l’objet saisi

Vous pouvez constater un premier problème : la sphère ayant un _rigidbody_, les forces continuent de s’y appliquer (notamment la gravité) lorsqu’on la saisit. Il est possible de contourner ce problème en jouant sur la variable _isKinematic_ mentionnée plus tôt. En mettant le booléen _isKinematic_ à true lorsqu’on saisit la sphère, la gravité ne s’y appliquera plus. Attention toutefois à remettre le booléen à false lorsque la sphère est relâchée !

#### Problème 2 : Comment savoir si un objet est saisissable ou non, saisi ou non

Une des grandes problématiques lorsqu’on travaille en environnement virtuel est de rendre les interactions possibles facilement compréhensible pour l’utilisateur. On imagine bien que dans un environnement plus riche que le nôtre, certains objets seront manipulables, d’autres non. De la même manière, il est intéressant d’avoir des indicateurs nous permettant de savoir si l’on est en train de saisir un objet ou non. Nous allons ici utiliser de très simples **métaphores visuelles** en jouant sur les curseurs. Nous vous proposons trois types de curseurs :

![image](https://hackmd.io/_uploads/HJALlgPKp.png)
Figure : Une croix verte = objet saisissable (non saisi)

![image](https://hackmd.io/_uploads/Hy7PllDFp.png)
Figure : Une croix rouge = objet saisi

![image](https://hackmd.io/_uploads/BJ9wggwt6.png)
Figure : Une croix noire : aucun objet saisissable détecté dans le rayon.

Enregistrez ces trois images dans un dossier « cursor » de votre projet. Pour pouvoir utiliser une image en tant que curseur, il faut que l’image correspondante fasse partie des assets, mais également que leur type soit considéré comme des curseurs de souris. Cliquez donc sur une des trois images que vous aurez importé dans vos Assets, et dans l’Inspector, modifiez la propriété _Texture Type_ pour lui donner la valeur _Cursor_. N’oubliez pas le petit « Apply » en bas – si vous oubliez, un pop-up s’affichera pour vous le signaler dès que vous cliquerez autre part. 

Vous pouvez désormais modifier votre script _Raycasting_ pour changer la texture du curseur lorsque le rayon détecte (ou pas) un objet, ou lorsqu’un objet est saisi.
- On utilisera 3 variables ``Texture2D`` pour y mettre les 3 curseurs :
```cshap!
public Texture2D cursorOff, cursorGrabbable, cursorGrabbed;
```
- La fonction [Cursor.SetCursor](https://docs.unity3d.com/ScriptReference/Cursor.SetCursor.html) permet de modifier le curseur de la souris


Vous devriez arriver à quelque chose comme ça :

![image](https://hackmd.io/_uploads/rkC_xxDtp.png)
Figure : aucun objet saisissable
![image](https://hackmd.io/_uploads/Hk_YggPYa.png)
Figure : la sphère est saisissable
![image](https://hackmd.io/_uploads/HJlqelDF6.png)
Figure : La sphère est saisie


#### Problème 3 : Le plan est un objet, mon programme le considère donc comme saisissable

Eh oui, si vous posez la balle et que vous visez le sol, vous pouvez voir que votre curseur passe au vert… N’essayez pas de cliquer, vous récolteriez une erreur… Pour l’instant, nous détectons tous les objets possédant un _collider_ et le plan en fait partie. Si vous avez jeté un œil à la fin du TP2, vous devriez avoir une idée concernant la solution : et si on utilisait un tag ? En créant un nouveau tag **Grabbable**, et en ajoutant un test sur ce tag dans votre script, vous devriez être capable de différencier la sphère (qui sera taguée **Grabbable**) du plan (qui lui ne le sera pas).

L’utilisation de ce tag a un autre avantage (et non des moindres) : vous pouvez désormais ajouter autant d’objets que vous le souhaitez sur votre scène, et définir lesquels seront saisissables en leur affectant le tag. A titre d’exercice, vous pouvez placer des cubes et des cylindres sur votre scène. Appliquez le tag **Grabbable** aux cubes mais pas aux cylindres. Tout devrait fonctionner sans problème.

:::info

**Une autre (et meilleure dans ce cas) façon** d’arriver au même résultat est d’utiliser l’argument_ [_layerMask_](http://docs.unity3d.com/Manual/Layers.html) _de la fonction_ [_Physics.Raycast_](http://docs.unity3d.com/ScriptReference/Physics.Raycast.html) afin qu’elle ne renvoie true que lorsqu’elle rencontre un objet présent sur le masque. Cela fonctionne de la même façon que les tags et vous pouvez en rajouter des personnalisés. Notez d’ailleurs qu’il y a un layer nommé « Ignore Raycast »… En revanche, pensez à prendre en compte que les [Layers](https://docs.unity3d.com/Manual/Layers.html) sont des bitmask, ce qui permet de les combiner !

- Pour cela, ajoutez un layer « Grabbable », et appliquez-le à chaque sphere (ou objet que vous souhaitez pouvoir attraper)

![image](https://hackmd.io/_uploads/S18iglvFa.png)


- Puis, dans votre script, rajoutez une variable :  
`private LayerMask mask;`

- Puis dans la fonction start(), récupérez le mask dans cet variable :  
`mask = LayerMask.GetMask("Grabbable");`

- et enfin, modifiez vos appels à ``Physics.Raycast`` :  


:::
## Pour aller plus loin

- GRABGRABGRAB

Amusez-vous à prendre et lâcher plusieurs fois la balle… Elle ne vous paraît pas de plus en plus grosse ? C’est le cas : elle se rapproche de vous quand vous l’attrapez ! En effet, si vous avez regardé ce que contient le RaycastHit pour calculer la distance initiale entre l’objet et la caméra, vous avez sûrement utilisé hit.distance… Cela calcule effectivement la distance entre le départ du rayon et le point de collision du collider, donc en surface ! Or, lorsqu’on déplace la balle, c’est par rapport au centre de celle-ci, et vous avez peut-être oublié (comme moi au début) de prendre cela en compte… C’est ce genre de petit détail qui vous donnera souvent le plus de fil à retordre dans vos projets, donc pensez toujours à tester, retester et tester encore !

Pour régler ça, regardez du côté de [Vector3.Distance()](https://docs.unity3d.com/ScriptReference/Vector3.Distance.html) pour calculer la vraie valeur de l’offset

- AddTorque

Une méthode pour déplacer la balle que nous n’avons pas employée est d’utiliser la fonction [AddTorque](http://docs.unity3d.com/ScriptReference/Rigidbody.AddTorque.html), qui permet de prendre en compte la spécificité des rotations pour des objets sphériques… En gros, AddForce() fait bien le taf, AddTorque() pour une sphère, le fait mieux ! ![😉](https://s.w.org/images/core/emoji/11/svg/1f609.svg)

![image](https://hackmd.io/_uploads/ByV2leDY6.png)

Figure : Crédits : http://craig.backfire.ca/pages/autos/horsepower

- Terrain

Depuis le début, nous utilisons de simples plans comme support de nos scènes. Vous pouvez un outil beaucoup plus évolué en utilisant un terrain plutôt qu’un plan **GameObject > 3D Object > Terrain**. Dans l’Inspector, vous aurez accès à de nombreuses propriétés vous permettant d’ajouter du relief, des arbres et bien plus encore. Pour en savoir davantage, c’est [ici](http://docs.unity3d.com/Manual/script-Terrain.html) que ça se passe. Vous pouvez également modifier le “ciel” en jouant sur la [_skybox_](http://docs.unity3d.com/Manual/class-Skybox.html).

## Solution du TP
- Téléchargez et importez le package suivant : [Package TP01](http://azed.in/courses/virtual_reality/tp/solutions/TP01.unitypackage)

## Suite
=> TP04 : https://hackmd.io/@azedin/H1ydXnxsp 