(En cours d'édition...)

Tuto Complet : Roll-A-Ball (Jeu de Bille)

Prise en main de Unity 3D

Ce tuto est une version adaptée de celui créé par Unity s'intitulant Roll-A-Ball, disponible ici.

 

C'est le premier que nous allons développer ensemble. Ce sera donc l'occasion de se familiariser autant avec l'interface de Unity, mais aussi avec le langage de programmation utilisé.

 

Le but est donc de réaliser un petit jeu dans le style de celui ci-dessous. Mais je vous invite à vous amuser et à largement personnaliser, améliorer le level-design de ce jeu en utilisant les notions techniques que l'on aura pu voir ensemble.

 

Dans l'état actuel, le jeu se déroule comme suit :

  • Le joueur contrôle la bille grâce aux flèches du clavier.
  • Il doit collecter tous les points en passant dessus.
  • Lorsqu'il passe sur un point, celui-ci disparaît et est comptabilisé. Le score est par ailleurs affiché.
  • Le joueur ne doit pas tomber. S'il tombe, la partie est réinitialisée.
  • Si le joueur arrive au portail sans avoir récupéré tous les points, il perd.
  • Il gagnera s'il arrive au portail en ayant récupéré tous les points.

Vous pouvez télécharger le fichier .unitypackage de ce projet afin de l'ouvrir dans Unity pour le tester et voir comment il a été cousu. Pour cela, dans Unity, créer un nouveau projet puis menu "Assets > Import Package > Custom Package".

Table des matière

1. Insérer votre premier GameObject dans la scène

  • Observer le fait que le gameobject est composé de plusieurs composants, visibles dans l'inspecteur.
  • Le composant principal est le Transform
  • Différentes façons d'appliquer une transformation : dans le viewport via le gizmo, sur le composant Transform par glissé ou en entrant les données numériques dans le Transform

2. Créer un Prefab

prefab
  • Faites un glisser / lâcher d'un gameobject depuis la hiérarchie de votre scène vers votre dossier "Prefabs" que vous aurez au préalable créé.

 

  • L'usage des Prefabs :
    • Un prefab est en quelque sorte une sauvegarde en l'état d'un objet. On s'en servira beaucoup pour créer des objets réutilisables dans le level design d'une scène.
    • Tout changement sur le prefab via la fenêtre de projet agira sur toute instance du prefab sur la scène.
    • Changer un prefab sur la scène ne changera QUE l’instance en cours et pas toutes les autres. Il faut mettre à jour le prefab via la fonction Apply et cela agira sur le prefab lui-même, enregistré dans vos dossiers (ce qui agira par la même sur toutes les autres instances).
  • Il faut voir un prefab comme un objet prêt à l'emploi et sauvegardé, quoi qu'il advienne.

3. construire le niveau à l'aide d'autres GameObjects

Le Snap

snap

Afin d'aligner parfaitement les objets entre-eux, il faut utiliser la fonction de snap. Maintenez la touche "V" du clavier appuyée, ce qui vous donne la possibilité de déplacer le gizmo au point d’accroche que vous souhaitez en survolant l'objet 3D avec votre pointeur.

Une fois que le gizmo est au bon endroit, cliquez et déplacez l'objet. Il viendra alors s’accrocher aux coins des autres objets. Tant que vous maintenez le clic appuyé, vous pouvez ajuster la position de l'objet, de façon parfaite.

Organisation de la hiérarchie

hierarchie, hierarchy

Lorsque l'on commence à éditer une scène et que les objets y étant présents se multiplient, on est très rapidement confronté au problème d'une hiérarchie illisible.

Il faut en fait Organiser cette hiérarchie.

 

Pour cela, utilisez des GameObjects Empty (vides) : Create > Create Empty

 

Vous pourrez alors attraper les éléments de la hiérarchie et les glisser à l'intérieur.

De cette façon, vous serez en mesure d'organiser votre scène, peu importe le nombre d'objets que vous y mettrez.

4. Les Skybox

skybox

Afin de créer un arrière plan esthétique, notamment un ciel, ou une vue d'espace, etc... Nous utilisons en 3D des Skybox. Sorte de matières étant toujours visibles par la caméra principale où que l'on regarde.

 

Si vous configurez votre skybox mais qu'elle ne se voit pas, c'est probablement que les effets ne sont pas actifs dans la scène d'édition. Vérifiez cela dans le petit menu "Effects".

paramétrer skybox

Dans Unity 4, il faut passer par le menu "Edit" puis "Render Settings" pour pouvoir paramétrer la skybox de votre scène.

 

C'est dans la case Skybox Material que vous allez pouvoir insérer la skybox de votre choix. Comme vous pouvez le voir dans l'image ci-contre, une skybox est en fait un matière. Il vous faut en avoir dans les dossiers de votre projet. Vous pourrez en télécharger sur l'asset store, mais vous pouvez aussi utiliser celles fournies par Unity en important l'un de leurs Standards Packages : menu "Assets" > "Import Package" > "Skyboxes".

Une fois l'import terminé, vous pourrez utiliser les skyboxes importées dans vos dossiers.



Edit :

Depuis Unity 5, il faut afficher la fenêtre de gestion de rendu des lumières : "Windows > Lighting".

Dans l'onglet "Scene", vous trouverez "Environment Lighting" puis la propriété "Skybox".

5. Editer des matériaux pour coloriser le niveau

Si vous souhaitez personnaliser les objets que vous avez inséré dans votre scène, il va pour cela falloir créer des matières.

On va ainsi pouvoir colorer les objets, mais aussi, les texturer (leur assigner des images, comme des briques pour les murs, du sable pour le sol, etc...)

Créer une matière

création matière material

Une matière est un fichier à créer dans les dossiers de votre projet.

Pour une bonne organisation, je vous conseille vivement de vous créer un dossier dédié : nommez-le "Materials", ou "Matières"... Il contiendra toutes vos matières.

 

Dans votre dossier destiné aux matières, faites un clic droit > Create > Material.

configurer material

Pour configurer votre material, sélectionnez-le dans la fenêtre projet ; ses paramètres s'affichent alors dans l'inspecteur.

 

Le Shader est en fait le "type de matière" : vous pouvez créer des matières transparentes, d'autres brillantes, etc...

 

Pour colorer une matière, cliquez sur le rectangle couleur. Ceci affiche le color picker.

show in explorer
Afficher l'explorateur de fichiers

Si vous souhaitez utiliser des textures, qui ne sont en fin de compte que des images, il vous faut au préalable en avoir dans votre dossier Project. Pour cela, plusieurs méthodes :

  • Utiliser le Unity Asset Store : Menu Window > Asset Store. Vous pourrez importer des packs de textures dans votre projet.
  • Rechercher des textures sur internet : utilisez pour cela le terme "texture" dans vos recherches. Par exemple : "texture sable" sur Google image. Il s'agit donc de télécharger l'image et de l'enregistrer dans votre projet. Il va pour cela falloir afficher votre dossier "Images" ou "Textures" de votre projet. Faites un clic droit dans votre dossier et utilisez la fonction "Show In Explorer", ce qui vous affichera l'explorateur de fichiers de Windows à l'endroit de votre projet. Il ne vous restera plus qu'à y placer vos textures par copie.

Appliquer une matière à vos objets 3D

appliquer matière material

Pour appliquer une matière à l'un de vos objets présents dans la scène, rien de plus simple ; 3 méthodes :

  • Attrapez votre matière depuis vos dossiers de projet puis glissez et lâchez sur l'objet, directement dans la scène.
  • Glisser / lâcher sur l'objet listé dans la hiérarchie.
  • Glisser / lâcher directement dans l'inspecteur.

Grâce à tout ce que l'on a pu voir là, vous êtes déjà en capacité d'éditer des niveaux de jeu assez évolués.

A partir de là, nous allons voir à mettre en place les éléments essentiels au fonctionnement du jeu.

6. Création des objets à collectionner

Pour matérialiser les points à collectionner, j'ai choisi d'utiliser de simples cubes.

L'idée, c'est d'exploiter le système des prefabs :

  1. Créer un cube et bien le position au point d'origine de la scène (coordonnées 0, 0, 0) - prenez cette habitude ; cela vous évitera bien des soucis.
  2. Lui appliquer un rotation de 45° sur tous les axes.
  3. Lui créer et lui appliquer une matière transparente (j'ai choisi le shader "Transparent / Specular" pour la brillance).
  4. Une fois ceci fait, en créer un prefab.

A partir de là, vous allez pouvoir peupler votre scène d'autant de points à collecter que vous le souhaitez.

Pour instancier un prefab, il vous suffit de la glisser / lâcher depuis votre dossier Project soit directement sur la scène, soit dans la hiérarchie.

Mais pour aller plus vite, vous pouvez aussi dupliquer tout objet de votre scène par un CTRL + D.

 

Concernant l'animation des cubes, il y a plusieurs façons de faire :

  • La plus intuitive serait d'utiliser les possibilités d'animation de Unity. Mais on verra cela plus loin pour les autres objets animés.
  • Ici, j'ai choisi d'animer mes cubes via un petit script de contrôle que j'ai nommé Rotator.

Le script Rotator

Nous allons donc créer notre premier petit script.

Créez-vous un dossier intitulé "Scripts" ; il contiendra tous les scripts de votre jeu. Un jeu peut comporter des dizaines, voir des centaines de scripts. Il faut donc être rigoureux au niveau de votre organisation.

 

Dans votre dossier "Scripts", faites clic droit > Create > C# Script

 

Attention à cet instant : ne nommez JAMAIS un script à l'aide d'espaces ; si vous souhaitez composer le nom de votre script de plusieurs mots, suivez cet exemple : "MonScript" et non "Mon Script".

 

Il faut savoir que le nom que vous donnez à cet instant devient en fait le "nom" du petit programme que vous allez créer. On appelle cela un classe. Et en programmation, un nom de classe ne peut avoir d'espace ; cela donnerait une erreur.

 

Créez votre script "Rotator". Afin qu'il agisse sur les cubes, glissez-le sur l'un d'entre eux. Afin que le prefab s'actualise avec nouveau changement, cliquez sur le bouton "Apply" au-dessus du composant Transform, dans l'inspecteur d'objet. Cela va mettre à jour par la même occasion toutes les autres instances de ce prefab dans la scène.

 

Ouvrez maintenant votre script en double cliquant dessus. Il va s'ouvrir dans MonoDevelop ou VisualStudio pour son édition.

 

Voici le script Rotator :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
using UnityEngine;
using System.Collections;

public class Rotator : MonoBehaviour
{
        //Variable à assigner via l'inspecteur. Elle nous sert à donner le sens de rotation de notre cube
        public Vector3 sensDeRotation;

        // Update is called once per frame
        void Update ()
        {
                //Utilisation de la fonction Rotate() de l'objet Transform en appliquant le vecteur 3 de rotation
                this.transform.Rotate ( sensDeRotation );
        }
}
assignation vecteur 3 via inspecteur

Je n'expliquerai pas ici en détail toutes les notions de base de programmation utilisées ici, mais nous allons quand même voir quelques petits éléments :

  • Tout d'abord, remarquez bien que la variable SensDeRotation est publique et donc à assigner via l'inspecteur. Il s'agit d'un vecteur 3, il y aura donc 3 valeurs à entrer (X, Y et Z).
  • Dans la fonction Update, boucle du jeu, nous accèdons au composant Transform du cube et utilisons la fonction Rotate (prenez l'habitude d'utiliser la documentation Unity pour savoir comment utiliser telle ou telle fonction).
  • Ici, la fonction Rotate requiert un Vecteur 3 pour fonctionner. Nous allons donc lui passer la variable sensDeRotation au préalable assignée dans l'inspecteur comme ci-contre.

Ainsi, à chaque frame du jeu, le cube subit une légère rotation, et ce, indéfiniment.

7. Setup de l'objet Player

Une variante un peu plus complexe et offrant plus de possibilités est dispo ici.

Le setup de base

setup player

L'objet "Player" va en fin de compte être la sphère que le joueur contrôlera.

 

Créez une sphère, placez-la  en 0, 0, 0, personnalisez-la à votre convenance.

 

Au niveau des composants nécessaires à son setup, il y aura l'ajout d'un Rigidbody pour utiliser le système de physique de Unity.

 

Concernant le composant Player Controller, il s'agit du script de contrôle que nous allons créer par la suite. Vous pouvez d'ores et déjà le créer dans votre dossier "Scripts", le nommer "PlayerController" puis l'attacher à la sphère.

 

Une fois ce setup en place, "sauvegarder" votre objet Player en créant un prefab dans votre dossier "Prefabs".

Le script de contrôle

Le fait que notre sphère soit dotée d'un Rigidbody nous permet l'utilisation d'une fonction très pratique :

RigidBody.AddForce ( Vector3 force ).

 

Le script agissant sur la sphère elle-même, il est donc possible d'accéder à son Rigidbody très simplement : this.rigidbody.

A partir de là, on trouvera la fonction AddForce().

 

Pour ce qui est de la force, elle est à passer sous la forme d'un vecteur 3. Cela donne la direction et la puissance.

Ici, j'ai utilisé Vector3.forward ; c'est comme si j'avais utilisé "new Vector3 ( 0, 0 ,1)".

De même pour Vector3.back = new Vector3 ( 0, 0, -1) ; etc...

 

Le repérage des touches du clavier se fait avec la classe Input de Unity.

On accède donc à la fonction GetKey de Input, ce qui nous permet de savoir si telle ou telle touche est pressée...

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
using UnityEngine;

public class PlayerController : MonoBehaviour
{
        //Référence au rigidbody de la bille
        public Rigidbody playerRigidbody;

        //Vitesse à assigner via l'inspecteur
        public float        speed;
        public float        jump;



        // Update is called once per frame
        void Update ()
        {
                //SI la touche "flèche du haut" est maintenue appuyée
                if ( Input.GetKey ( KeyCode.UpArrow ) )
                {
                        //Ajout de force vers l'avant au rigidbody
                        playerRigidbody.AddForce ( Vector3.forward * speed );
                }

                if ( Input.GetKey ( KeyCode.DownArrow ) )
                {
                        playerRigidbody.AddForce ( Vector3.back * speed );
                }

                if ( Input.GetKey ( KeyCode.LeftArrow ) )
                {
                        playerRigidbody.AddForce ( Vector3.left * speed );
                }

                if ( Input.GetKey ( KeyCode.RightArrow ) )
                {
                        playerRigidbody.AddForce ( Vector3.right * speed );
                }

                if ( Input.GetKeyDown ( KeyCode.Space ) )
                {
                        playerRigidbody.AddForce ( Vector3.up * jump );
                }
        }
}

Dernier détail : si vous souhaitez donner simuler une bille un peu plus lourde, vous allez devoir utiliser un nouveau composant. Il s'agit du Constant Force, que vous trouverez dans Add component > Physics > Constant F orce.

 

Pour simuler du poids, l'idée est d'ajouter une force constante poussant vers le bas. Donc, un vecteur 3 dont le Y sera négatif : une valeur de -10 fonctionne bien.

La caméra

Pour que la caméra fonctionne, cela va être un peu plus pointilleux.

En effet, on serait tenté d'accrocher la caméra à l'objet Player.

  1. Positionnez votre Player en 0, 0, 0
  2. Positionnez la caméra de sorte à bien y voir
  3. Placez la caméra dans le Player

Essayez ce setup...

Plutôt drôle, non !?

Mais bon... C'est totalement injouable...

 

Effectivement, nous allons avoir besoin d'utiliser autre chose pour que notre caméra suive la sphère du joueur.

Alors, il faut quand même savoir qu'un setup de ce type fonctionnerait parfaitement si le personnage joueur était autre chose qu'une bille roulante.

Mais dans notre cas, c'est à l'aide d'un script que nous allons pouvoir faire suivre cette caméra.

 

Commencez par ressortir la caméra du Player puis positionnez-la de sorte à bien voir la sphère. Enfin, créez un nouveau script que vous appellerez par exemple CameraController et placez-le sur la caméra.

 

Bien ; nous sommes prêts à coder !

 

Mais avant de coder quoi que ce soit, il faut d'abord comprendre concrètement ce que nous voulons que le script fasse :

  1. Au lancement du jeu, il doit calculer l'offset : le décalage entre la caméra et la sphère.
  2. Le script devra maintenir cet offset entre la caméra et la sphère, quels que soient les déplacements du joueur.

Il s'agit d'un calcul géométrique : il faut calculer le vecteur représentant l'écart entre deux points. On prend le vecteur de coordonnées du premier point et on lui soustrait le vecteur du second.

  • Offset = position de la Caméra - position de la Bille

Il faut ensuite maintenir le décalage tout au long du jeu :

  • Position de la caméra = position de la Bille + Offset (le décalage calculé)

Afin de mettre en place ce calcul sur la caméra, il va falloir créer un script ; on l'appellera CameraController, car ce script va réellement contrôler la caméra, c'est lui qui déplacera la caméra selon le calcul donné.

Une fois le script créé, il faut bien entendu l'attacher à la caméra.

 

Voici ce qu'il nous faut inscrire dans ce script :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using UnityEngine;

public class CameraController : MonoBehaviour
{

        public GameObject        player;     //Référence au gameobject Player (à assigner dans l'inspecteur d'objets)

        private Vector3          _offset;        //Offset de la caméra à maintenir par rapport au player


        // Use this for initialization
        void Start ()
        {
                //Le décalage est égale à la position de la caméra - la position du Player
                _offset = this.transform.position - player.transform.position;
        }
        
        // Update is called once per frame
        void Update ()
        {
                //Maintient de l'offset chaque frame
                this.transform.position = player.transform.position + _offset;
        }
}

8. Création des billes gênantes

Maintenant que notre bille nous obéit bien et que la caméra la suit partout où elle va, on va ajouter au niveau quelques éléments gênants.

Afin de créer les billes et de donner l'impression qu'elles sont plus lourdes que la bille du joueur il va falloir jouer avec la propriété "Mass" du Rigidbody : tous les détails ici.

1. Commençons par créer une simple sphère dans la scène
1. Commençons par créer une simple sphère dans la scène
2. La paramétrer : taille, matière personnalisée, bien la nommer dans la hiérarchie, etc...
2. La paramétrer : taille, matière personnalisée, bien la nommer dans la hiérarchie, etc...

Comme je le disais, afin que cette bille paraisse plus lourde qu'une autre, il faut que la propriété "Mass" soit supérieure à celle de l'autre.

Par exemple, ici, je lui ai mis une Mass de 5 alors que la bille du joueur est réglée à 1. L'effet fonctionne bien.

 

ATTENTION : La propriété "Mass" n'a rien à voir avec le poids. Elle ne sert que lorsque des objets se touchent entre eux pour savoir lequel a la plus grosse masse. Mais cela n'agira pas en termes de gravité.

Pour finir le setup, il ne nous reste plus qu'à créer un prefab. Une fois fait, instanciez autant de fois que vous le souhaitez votre bille gênante.

9. Animer les éléments du décors

Nous allons voir ici une utiliser basique du composant Animation de Unity afin de créer des objets animés dans notre scène. Nous verrons ensuite comment créer un objet animé réutilisable, grâce au système de prefab.

Le setup de base

Afin de créer plusieurs objets animés de façon identique, l'idée serait de faire un setup qui fonctionnerait partout où je souhaite disposer l'objet animé en question.

Nous connaissons le principe des prefabs ; nous allons avoir besoin d'exploiter ce concept.

 

Tout d'abord, mettons en place notre objet dans la scène. Faisons ce que l'on appelle le setup :

Pour l'exemple, j'ai choisi d'utiliser un simple cube, redimensionné pour symboliser une barre.

Ce que je souhaite, c'est que cette barre fasse un va-et-vient, gênant ainsi le joueur à traverser le passage.

Cette barre, je souhaite donc l'animer. Dans Unity, il existe un composant s'intitulant "Animation". C'est celui que l'on va voir ensemble. Vous le trouverez dans la catégorie "Miscellaneous".

 

Attention de ne pas utiliser le composant "Animator" ; il ne sert pas du tout à la même chose.

Il est effectivement en lien avec l'animation, mais il ne sert pas créer des animations.

Il s'agit en fait d'un genre de gestionnaire d'animations.

Le composant Animation

Voyons maintenant comment le composant Animation se présente :

Etant donné ce que l'on veut faire, la propriété qui va le plus nous intéresser est "Animation". Comme vous pouvez le voir, Unity attend ici un "Animation Clip". Pour le moment, il n'y a rien ("None").

 

Ce clip d'animation est un fichier que l'on va devoir créer grâce à l'éditeur d'animation. Une fois qu'il aura été enregistré dans les dossiers du projet, il faudra ensuite le glisser dans cette zone.

 

L'option "Play Automatically", si elle est cochée, fera que le composant jouera l'animation en question automatiquement au lancement du jeu (ou du moins, pour être exact, à l'instant où le GameObject se retrouvera dans la scène - étant donné que le notre y est déjà, il s'animera donc instantanément).

Fonctionnement du composant

Voici un petit schéma de ce qu'il faut bien comprendre lorsque l'on veut utiliser de l'animation dans un jeu.


Comme je le disais, nous allons devoir créer un clip d'animation. Celui-ci sera enregistré dans vos fichiers de projet. Ce clip devra être inséré dans la propriété "Animation" du composant Animation du GameObject. Alors, l'objet de la scène 3D sera animé selon le fichier d'animation.

Schéma de fonctionnement du composant Animation
Schéma de fonctionnement du composant Animation

L'animation Clip

Maintenant que vous avez compris comment utiliser le composant Animation, il est temps de créer l'animation à proprement dite.

 

Avant toute chose, nous avons besoin de la fenêtre Animation :

Window > Animation.

Je vous conseille de la docker au-dessous du viewport 3D.


La fenêtre Animation étant désormais bien visible, nous allons pouvoir créer notre fichier d’animation (ou Animation Clip).

Lorsqu'un GameObject est équipé du composant Animation, il suffit de le sélectionner dans la hiérarchie pour que le bouton "Create" apparaisse dans la fenêtre d'animation.


Cliquez sur "Create". Il vous est alors proposé d'enregistrer un fichier ".anim". Si ce n'est déjà fait, créez-vous un dossier "Animations" dans lequel vous rangerez toutes les animations que vous créerez.

Enregistrez-y maintenant votre fichier d'animation. Personnellement, j'ai choisi comme nom : "MouvementBarre.anim".


A l'instant précis où le fichier est enregistré, vous remarquerez que votre fenêtre d'animation se met en mode édition.

Observez par ailleurs les boutons de contrôle du jeu qui se mettent en rouge. Ceci signifie que tout ce que vous allez faire dans la scène sera enregistré dans l'animation... Donc attention ! ^^

La technique de KeyFraming

Petit point théorique sur la technique d'animation utilisée dans Unity :


Il faut savoir que l'animation dans Unity va se faire comme dans tous les autres logiciels 3D : à savoir, en utilisant la technique que l'on appelle le "KeyFraming".


Qu'est-ce que cela veut dire : Il s'agit en fait de dire au logiciel qu'à un instant "t1" l'objet sera, par exemple, à telle position dans la scène, et qu'à un second instant "t2", l'objet sera à un autre emplacement de la scène. Ces deux positions à ces deux moments différents forment ce que l'on appelle des clés d'animation, ou "keys" en anglais (d'où KeyFraming).


A partir de là, Unity va en fait s'occuper tout seul de créer ce qui s'appelle l'interpolation entre ces deux clés. C'est à dire qu'il va calculer toutes les positions que l'objet doit prendre à chaque frame de l'animation entre l'instant "t1" et linstant "t2" pour que celui-ci effectue un mouvement fluide du point A au point B sur la ligne de temps fixés par les clés (d'où le KeyFraming).


L'interpolation calculée peut être de type mouvement, comme on vient de le voir, d'un point A à un point B, mais aussi, de type scale, rotation, ou toute autre valeur paramétrable de votre objet, et donc, animable.

Il nous est alors possible d'animer aussi bien des déformations d'objet que des mouvements, des valeurs de couleurs, de transparence, etc...

La fenêtre d'édition des animations

Plutôt qu'un long discours, j'ai préféré vous mettre ici un schéma explicatif de la fenêtre d'édition :

Edition du Clip d'Animation de base

  • Pour commencer, assurez-vous d'avoir bien sélectionné le bon objet dans la hiérarchie.
  • Cliquez sur la barre de temps et positionnez-là où vous le souhaitez. Ici, à zéro. Notez que l'éditeur s'est automatiquement mis en mode "Enregistrement". Comme je le disais plus haut, tout ce que vous allez faire dans la scène sera enregistré dans la timeline. Voyons comment cela fonctionne :
  • Faites un 1er déplacement de votre objet, dans la scène. Observez ce qui s'est passé dans la timeline : une 1ère clé s'est automatiquement positionnée. C'est la propriété "Position" de l'objet qui a été enregistrée. Si on la déroule, on peut voir les valeurs enregistrées.
  • Maintenant qu'une 1ère clé est enregistrée, positionnez la tête de lecture à une 2ème position temporelle, comme à 30s, puis placez votre objet à une autre position dans la scène.
  • A ce moment-là, vous dites à Unity que vous souhaitez que votre objet soit à la position 2 ou bout de 30s.
  • Testez votre animation en baladant la tête de lecture de gauche et de droite.
  • Testez l'animation à sa vitesse réelle avec le bouton "Play" de l'éditeur d'animation.

Si vous testez votre animation, vous constaterez qu'à chaque que la tête de lecture arrive à la fin, celle-ci revient au début. C'est ce qui se passera dans le jeu si on souhaite faire "boucler" l'animation.

Le problème, ici, c'est que notre barre se retrouve littéralement "téléportée" à son 1er emplacement puis réitère son déplacement.

Afin que l'animation boucle bien, il existe 2 solutions :

  • 1ère solution : Editer l'animation elle-même, en y ajoutant une 3ème clé, identique à la 1ère. C'est ce que l'on va faire en 1er.
  • 2ème solution : Nous allons avoir besoin d'indiquer à Unity, via certains paramètres, que cette animation DOIT boucler. Il y a plusieurs options, nous le verrons plus bas, mais l'un d'eux nous permettrait de lire l'animation un coup dans le sens normal, puis dans l'autre, etc... etc... En va-et-vient autrement dit. C'est en fait la meilleure solution, dans notre cas.

Voyons d'abord comment il est possible d'éditer les clés directement dans la timeline.

Concrètement, ce que l'on veut, c'est que la barre revienne EXACTEMENT à la même position qu'au début. Pour cela :

  1. Sélectionnez la première clé.
  2. Copiez : CTRL + C
  3. Positionnez la tête de lecture au temps souhaité (ici, 1s).
  4. Collez : CTRL + V
  5. Testez votre animation

Voilà, de cette façon, l'animation fait bien un va-et-vient. Nous allons pouvoir la "faire boucler" parfaitement.


 Il ne nous reste plus qu'à tester : Sortez du mode édition et appuyez sur Play ; observez le résultat.


L'animation est jouée ! Parfait !

L'ennui, c'est qu'elle ne "boucle" pas : elle est jouée une fois et une seule.


Il y a en fait un dernier réglage à faire.

Il est situé sur le fichier d'animation lui-même.

Paramétrage du fichier d'animation

Afin de paramétrer une animation, il faut avant tout la localiser dans vos dossiers de projet.

Sélectionnez-le puis observez les paramètres inscrits dans l'inspecteur.

En paramétrant le Wrap Mode, il est en fait possible de dire à Unity comment doit réagir l'animation une fois que sa lecture est terminée. On peut l'entendre comme des modes de lecture de l'animation :

  • Default : L'animation est lue une fois et s'arrête.
  • Once : C'est la même chose (c'est en fait Once qui est utilisé par défaut).
  • Loop : Nous y voilà ! C'est ce mode qui permet de lire une animation en boucle.
  • Ping Pong : Ce mode lit l'animation une fois dans le sens normal, puis dans le sens inverse à l'infini.
  • Clamp Forever : Parfaitement inutile.

Faire boucler l'animation : 2 solutions

Je vous ai parlé plus haut de 2 solutions pour notre animation. Nous avons vu ensemble la 1ère : construire un va-et-vient dans l'animation elle-même, puis faire boucler en utilisant le paramètre Wrap Mode en Loop.


La 2ème solution aurait été d'exploiter le Wrap Mode Ping Pong :

Dans notre cas, ne créer que les deux première clés d'animation, ainsi notre barre irait de la gauche vers la droite. Enregistrer l'animation en l'état puis configurer le Wrap Mode.

L'animation aurait alors été lue dans un sens puis dans l'autre, ainsi donc, de la gauche vers la droite puis de la droite vers gauche, et ce, à l'infini !

Réutiliser l'objet animé dans la scène

Le setup élaboré jusque-là fonctionnera très bien pour un objet à animer.

Si l'on souhaitait créer d'autres objets animés dans la scène, il nous faudrait, en suivant cette méthode, créer autant d'animations que d'objets à animer. Cela fonctionne très bien.


Mais là où cela deviendra fastidieux, c'est si l'on souhaite animer un certain nombre d'objet avec une animation identique. Pour notre exemple, on serait tenter de copier la barre animée et de la placer ailleurs dans la scène, accompagnée de son animation... Et bien cela ne marchera pas : toutes les copies feront l'animation... au même endroit ; autrement dit, toutes les copies joueront bien l'animation, mais l'animation en question les "téléporte" en fait exactement aux mêmes coordonnées de la scène.


Afin de réutiliser en série des objets animés, nous allons préparer un prefab.

L'idée, pour que cela soit possible, c'est de placer l'objet animé dans un GameObject vide ; un "empty".

  • Créez un empty, pensez bien à le remettre à zéro !
  • Renommez-le de façon évidente (j'ai mis ici "Conteneur", mais ce n'est pas forcément le plus judicieux...)
  • Puis positionnez votre objet animé dans le conteneur.
  • Créez ensuite un prefab du conteneur (que j'ai par ailleurs renommé en "Conteneur Barre Animée").

Maintenant que vous avez ce setup au point, il vous est possible d'utiliser de façon illimitée votre prefab dans toutes les scènes de votre jeu !!!

Au passage, pensez à bien organiser votre hiérarchie.

En voici un exemple : Tous mes objets animés dans l'empty nommé "Objets Animés".

Et nous y voilà enfin !

Avec tout ceci, il vous est possible d'animer n'importe quoi dans votre jeu, de dupliquer des objets animés, etc...

 

N'hésitez pas à jouer de clés d'animation pour créer des animations plus longues, plus complexes, essayez l'animation d'autres paramètres que les simples coordonnées de position des objets (ex : le scale, la rotation, l'activation ou non d'un élément, etc..)

 

Dernière piste : Il vous est aussi tout à fait possible d'animer un objet lui-même composé de plusieurs sous-objets.

Exemple :

J'ai ici un objet composé de 5 sous objets.

Le composant Animation est donc positionné sur le GameObject appelé ici "Objet composé".

Dans Unity, il m'est tout à fait possible d'animer chacun des objets composant l'objet principal. Ce qui peut donner ce genre d'affichage de timeline :

10. Création du point de sortie

L'idée : Créer une zone évidente et repérable par le joueur où, lorsqu'il passe dessus, il se retrouve dans le niveau suivant.

 

Le concept : Un objet doté d'un composant Particle System et d'un Collider que le script du joueur repairera. S'il repère un contact, chargement du niveau que l'on veut.

 

Mise en place :

  • Créez un Empty.
  • Nommez-le par exemple "END" (attention, on se servira du nom dans le script).
  • Ajoutez un Box Collider.
  • Paramétrez-le en Trigger.
  • Positionnez le GameObject "END" où vous souhaitez dans la scène.
  • Editez la forme du Collider pour l'adapter à vos besoin.

A ce stade-là, nous pourrions d'ores et déjà rajouter notre ligne de script permettant le chargement du niveau suivant ; cela fonctionnerait, mais le joueur ne voit littéralement pas le point de sortie... pour le moment.

Il s'agit donc de rendre visible et "jolie" cette zone de passage au niveau suivant.

Pour l'exemple, et parce que c'est un outil d'effets spéciaux très puissant, nous allons utiliser un système de particules.

  • Ajoutez le composant Particle System au gameObject "END" (Effects / Particle System)
  • Expérimentez les différents paramétrages afin de trouver quelque chose qui vous plaît.

Voici un schéma de quelques-uns des paramètres sur lesquels vous pouvez jouer : (Cliquez ici pour la documentation complète)

Nous ne verrons pas ici tous les paramètres suivants. Il faut simplement comprendre que Unity les a divisés en modules. Nous allons expérimenter les deux premiers ; ceux-ci étant des modules essentiels. Puis nous allons tester certains modules basés sur le temps de "vie" d'une particule.

Voici un schéma expliquant ces modules :

Maintenant que votre point de sortie est prêt, il ne reste plus qu'à repérer le contact et charger le niveau.

 

Le morceau de script ci-dessous est à ajouter à votre script de contrôle de la bille (c'est le collider de la bille qui va repérer le contact avec un autre objet).

 

Afin de repérer le contact, nous allons avoir besoin de la fonction OnTriggerEnter. (Je rappelle que nous avons au préalable configurer le collider du point de sortie en Trigger).

Voici comment repérer le contact avec le gameObject nommé "END" (Attention, adaptez le nom selon celui que vous avez utilisé).

Placez cette fonction en dessous de la fonction Update() de votre script.

1
2
3
4
5
6
7
8
9
void OnTriggerEnter ( Collider objet )
{
        //Si le nom de l'objet touché est égal au nom du point de sortie
        if ( objet.name == "END" )
        {
                //Charger le niveau indiqué
                Application.LoadLevel ( "NomDuNiveau" );
        }
}

Application.LoadLevel ( "NomDuNiveau" ), vous l'aurez compris, est le code permettant de charger le niveau que l'on souhaite.

 

Attention tout de même, il est nécessaire d'avoir listé votre niveau dans "File > Build Settings" : glisser / lâcher vos scènes depuis vos dossiers de projet dans la boîte de dialogue.

 

"NomDuNiveau" doit être exactement écrit pareil.

 

Remarque : Cette fonction est bientôt obsolète. Il sera bientôt obligatoire d'utiliser la fonction :

SceneManager.LoadLevel ( " NomDuNiveau " )

Remarquez qu'elle fonctionne pareil...

(En cours d'édition...)

11. Collecter les points

  • Documentation du composant Collider : cliquer sur le petit point d’interrogation, dans l’inspecteur, en ayant un Point préalablement sélectionné.
  • Une fois sur la page du collider, il est possible d’accéder à toutes les informations liées au script de l’objet en cliquant sur « Switch to script » (et vice et versa en cliquant sur « Switch to Manual »).
  • Repérer l’évènement OnTriggerEnter (trigger = déclencheur)
  • Lorsque le Collider du gameobject Player sera en contact avec un trigger

(En cours d'édition...)

12. Afficher les points

  • Utilisation du nouveau système UI de Unity ; aussi appelé uGUI
  • Dans le script, cela signifie d’utiliser le namespace UnityEngine.UI afin d’accéder aux nouvelles fonctionnalités
  • Ceci est plutôt maladroit, mais pour ne pas créer trop de scripts différents, ces fonctionnalités ont été intégrées au script de contrôle de la bille

13. Le joueur atteint le point de sortie

  • Stopper les contrôles du joueur (voir script PlayerControler.cs)
  • Vérifier s’il a collecté tous les points
  • Si c’est bon, afficher « GAGNE ! »

14. Le joueur tombe

  • Créer une zone où, si le joueur la touche, il aura perdu
  • Créer un plane
  • Si le joueur touche cette zone
  • Recharger automatiquement le niveau (voir script PlayerController.cs)

15. Aller plus loin - Améliorer le jeu

  • Ajout de niveaux (utiliser Application.LoadLevel ("nom du niveau") pour charger un niveau de jeu)
  • Ajout de hauteur (saut entre plateaux placés les uns en dessous des autres)
  • S’amuser sur le level design !
  • Ajout de pentes, de montées…
  • Ascenseurs
  • Ajout de difficultés
  • Et pourquoi pas contrôler non pas la balle, mais le décor entier ! ^^

Annexes

Voici le script PlayerController. N'ayez pas peur de sa longueur, vous verrez qu'il y a vraiment beaucoup de commentaires !

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
using UnityEngine;
using UnityEngine.UI;                                                     //Permet l'utilisation du composant UI de Unity (uGUI)
using System.Collections;

public class PlayerController : MonoBehaviour {

        public float              speed;                                  //Variable de vitesse à éditer dans l'inpecteur d'objets

        public Text                      pointTextUI;
        public Text                      gameOverTextUI;
        public Text                      gameOverTextSuppUI;
        public Text                      winTextUI;

        private int                       points;


        void Start ()
        {
                points = 0;              //Afin d'être sûr de commencer à zéro, initialisation de la variable points à 0

                gameOverTextUI.enabled = false;
                gameOverTextSuppUI.enabled = false;
                winTextUI.enabled = false;

                UpdateDisplayingPointsText ();
        }

        // Update is called once per frame
        void Update ()
        {
                //Pour le controle de la boule, utilisation de Input.GetAxis() (voir doc)
                //Input.GetAxis() : Faire le lien avec Project settings / Input
                
                //Pour bien se rendre compte de ce qui est retourner :
                //Debug.Log ( Input.GetAxis ( "Horizontal" ) );          //Retourne un float oscillant entre -1 et 1 ; sera notre base de calcul

                float moveHorizontal = Input.GetAxis ( "Horizontal" );
                float moveVertical = Input.GetAxis ( "Vertical" );

                //Appliquer une force au Rigidbody (voir doc)
                //Observer la signature de la fonction : void (ne retourne rien), vector3 (a besoin d'un verteur 3 pour ajouter la force)
                //Observer l'exemple de code
                //Il y a 2 signatures => deux utilisations possibles

                //Quest-ce qu'un vecteur 3 ? (voir doc, mais ne pas s'y attarder)
                //C'est un objet composé de 3 valeurs : une pour le x, une pour le y et une pour le z
                //3 valeurs applicable à un espace 3D, composé des axes X, Y et Z
                //On va donc appliquer une force sur ces trois axes

                //Avant d'appliquer un vecteur 3 à notre rigidbody, on va d'abord le créer
                //New Vector3() => Voir doc
                Vector3 movement = new Vector3 ( moveHorizontal, 0, moveVertical );

                //Expérimenter cet état : AddForce ( movement ) => trop lent
                //Multiplier par une valeur : AddForce ( movement * 100 ) => tester différentes valeurs ;)
                //Utilité de créer une variable !
                //Créer public float speed
                
                //Afin d'être sûr que la vitesse sera la même sur tous les ordinateurs, on est obligé de multiplier par le tps écoulé entre chaque frame
                //Multiplier par Time.deltaTime
                //Ne pas trop s'y attarder, un peu complexe à expliquer...

                this.rigidbody.AddForce ( movement * Time.deltaTime * speed );
        }


        void OnTriggerEnter ( Collider other )
        {
                //Si l'objet touché est taggué "Point"
                if ( other.tag == "Point" )
                {
                        other.gameObject.SetActive ( false );            //Déssactivation du go
                        points = points + 1;                                             //Incrémentation de 1 point

                        //Debug.Log ( points );                                            //Observer l'incrémentation dans la console

                        //Plusieurs syntaxes d'incrémentation possibles
                        //points += 1;
                        //Dans le cas d'une incrémentation de 1 : points++;

                        UpdateDisplayingPointsText ();
                }

                //Si l'objet touché s'appelle "FIN"
                if ( other.name == "FIN" )
                {
                        if ( points >= 23 )                                                                            //Si le joueur a collecté tous les points
                        {
                                StopPlayerControl ();                                                           //Plus de contrôle par le joueur

                                winTextUI.enabled = true; ;
                        }
                        else                                                                                                     //Sinon
                        {
                                StopPlayerControl ();                                                           //Plus de contrôle par le joueur
                                
                                gameOverTextUI.enabled = true;
                                gameOverTextSuppUI.enabled = true;
                        }
                }

                //Si l'objet touché est la zone du vide
                if ( other.name == "VIDE 1" )
                {
                        gameOverTextUI.enabled = true;
                }

                //Si l'objet touché est la zone du vide
                if ( other.name == "VIDE 2" )
                {
                        Application.LoadLevel ( "Level 01" );                                  //Recharger le niveau
                }
        }

        //Méthode pour empêcher le joueur de contrôler la balle et la stopper
        private void StopPlayerControl ()
        {
                this.rigidbody.isKinematic = true;                //Assigner la propriété isKinematic à true permet de faire en sorte à ce que le moteur physique cesse d'agir sur l'objet
                this.enabled = false;                                     //Le mot clé thie cible ce script, que l'on désactive par la propriété enabled assignée à false
        }

        public void UpdateDisplayingPointsText ()
        {
                pointTextUI.text = "Points : " + points + " / 23";
        }
}

Écrire commentaire

Commentaires: 1
  • #1

    Valérie Tousignant (mercredi, 25 janvier 2023 20:25)

    Bonjour,
    Je suis conseillère pédagogique dans une école secondaire au Québec et nous voulons intégrer Unity dans un cours d'art et multimédia. Votre tuto, inspiré de celui de Unity est tout simplement merveilleux. Est-ce que nous avons l'autorisation de le prendre et le modifier à notre guise ?
    Bien à vous.
    Valérie
    valerie.tousignant@cdsl.qc.ca