Controller TPS

Dans cette activité les makers vont apprendre à faire un controller de type TPS (third person) avec des déplacements qu’ils vont coder et un personnage low Poly humanoid.

Objectifs pédagogiques

Compétences design

  • Coder, modéliser et animer un personnage principal dans Unity

Compétences techniques

  • Modélisation blender
  • Animation Blender/Unity
  • Importer des animations Mixamo

La place dans le module

Cette activité est la première du module.

Jour 1

Jour 2

Jour 3

Jour 4

Jour 5

1h

🤩

🤩

30 min

🤩

🤩

30 min

🤩

1h

🤩

Matériel

  • Blender
  • Unity
  • Visual Studio Code

Déroulé de l'activité

Segment 1 Modélisation du personnage (1h)

1/ Introduction (5 min)

Les makers vont créer un nouveau projet Unity pour pouvoir réaliser le controller. Pendant que le projet charge ils vont se rendre sur blender et modéliser le personnage.

On peut aussi leur montrer la petite démo suivante:

2/ Modélisation: preparation (15 min)

Dans Blender on va demander aux makers de préparer un elements qu’ils n’ont pas encore utilisé: un vertex seul. (Add > Mesh > Single Vert)
Sur ce vertex on va ajouter les modifiers qui vont permettre de simplifier une grande partie de la modélisation d’un humanoide:

  • Mirror
  • Skin (bien cocher la case « smooth shading »)
  • Subdivision Surface


Attention
: toutes les manipulations se feront en mode « vertex » et il faudra activer ou desactiver le clipping du Mirror selon les moments.

A partir de la, la modélisation est très simple!

Il suffit d’extruder son vertex pour former les membres, le torse, etc, de son personnage.

Lors de l’extrusion des bras, il faut désactiver le clipping du Mirror. Si on travaille sur le torse (ou autre partie centrale du personnage) activer le clipping.

Pour controller l’épaisseur d’une partie du personnage, il faut utiliser le raccourci: CTRL + A (ne pas hésiter à ajouter des vertex en plus avec Loop Cut, si on veut un meilleur control des épaisseurs)

2/ Modélisation (40 min)

Les makers vont modéliser leur personnage, on leur laisse bien le temps de faire, on leur remontre si besoin des étapes.

Segment 2 Habillage (30min)

1/ Habiller son personnage (30 min)

Ils pourront aussi choisir les zones de materiels (pour habiller le personnage en choissant une zone « pantalon », « t-shirt » etc

Avant de passer à l’étape suivante il faudra exporter son personnage en FBX

File > Export > FBX

Les paramètres sont les suivants, et on fait exporter dans un dossier facile à retrouver! (Telechargement, bureau, etc)

Segment 3 Rigging (avec mixamo) (30 min)

1/ Utiliser mixamo (15 min)

On prète aux enfants un compte mixamo de magic makers qui leur permettra de rigger leur personnage.
Si ils ont un compte google qu’ils préfèrent utiliser cela marche aussi.

compte1@magicmakers.fr

MM_00_mix

(c’est un double zéro dans le mdp)

Une fois sur le site les makers vont uploader leur modèle FBX (bouton upload sur la droite)
Puis suivre les instructions en plaçant les marqueurs de couleurs aux endroits indiqués. (on peut aussi choisir de changer le LOD en bas, c’est le nombre de doigts articulés du personnage)

On attend ensuite que Mixamo fasse sa magie! O.O

2/ Récuperer ses animations (15 min)

Une fois le personnage riggé, les makers peuvent alors récuperer toutes les animations dont ils pensent avoir besoin! On peut chercher des animations par noms en haut à gauche, et les telecharger avec le bouton download à droite. (pas besoin de toucher les paramètres)

Une petite liste des animations necessaires à minima:

  • Idle
  • Walk/Run (on peut ne prendre qu’une de ces deux la, et tricher avec la vitesse dans le code)
  • Jump
IMPORTANT: lors du telechargement mixamo, sur les animations de type « Marche » etc, à bien cocher la case « In Place » sinon les makers auront des mauvaises surprises!

Segment 4 Animation (1h)

1/ Importer les animations mixamo (5 min)

Les makers vont pouvoir drag’n’drop les animations téléchargées depuis Mixamo dans le dossier Unity.

2/ Preparation (10 min)

On va preparer le personnage, de la même façon que l’on avait préparé Blobby!

On commence par generer l’avatar. En selectionnant l’animation par défaut (normalement idle) on prepare les paramètres suivant:

Une fois l’avatar fait, on va preparer un arbre d’animation. On va donc créer un « Animator Controller » dans notre dossier Unity.
(c’est aussi le moment de ranger tout ce qui se raporte aux animations dans un dossier spécifique!)

Enfin, on tire le personnage de Idle dans le niveau, pour voir ce qu’on est en train de faire. Ce personnage possède un component « Animator » dans lequel on met notre « Animator Controller » tout neuf.

On double clic alors sur le controller, et on entre dans le vif du sujet.

3/ Arbre d’animation (45 min)

Ceci est une démo active, les makers vont faire en même temps que l’animateur, donc il faut bien prendre son temps pour leur montrer les étapes.

Si besoin, une grande partie de ces informations se retrouve dans les ressources:

ICI

On commence par prendre toutes les animations de notre dossier, et les trainer dans la zone de travail du controller (RENOMER LES ANIMATIONS SI BESOIN!)

On va ensuite connecter les animations (clic droit > transition) entre elle, avec une logique simple:

Quelle animation peut se déclencher après ou avant quelle animation?

Par exemple:

Jump doit pouvoir se déclencher n’importe quand. On connecte donc à Any State.

Idle et Walk doivent pouvoir passer de l’un à l’autre. On les connecte donc entre eux.

Par défaut Idle est l’animation de base, on fait donc clic droit « Mark as default » dessus.

Une fois les connections faites, on passe sur les paramètres.
En haut à gauche de la fenêtre on a un onglet parametre.

On va en créer 2, qui vont servir à déclencher les animations au bons moments:

  • vitesse (un float)
  • saut (un bool)

Une fois les paramètres fait, on va les appliquer.

Quand on selectionne une transition, on à a droite en bas une liste de Conditions.
On peut choisir un paramètre et donner des conditions pour ce dernier.

Encore une fois c’est une question de logique:

  • On passe de Idle à Walk si la vitesse est plus grande que X (environ 1 en general)
  • On passe de Walk à Idle quand c’est l’inverse
  • On active Saut simplement en activant/desactivant le bool si on est dans les airs

Quand tout ça est pret, ça y’est on peut passer au code!

Segment 5 Code du TPS (1h)

1/Mouvements: preparation (5 min)

Avant de coder le personnage on à une dernière étape à faire.

On va commencer par créer une capsule Unity, et lui donner un rigidbody. On va aussi lui retirer son renderer.
Ensuite on va parenter notre personnage animé à cette capsule, en s’assurant que le personnage rentre bien dans le collider.

Si besoin on scale le personnage (MAIS PAS LA CAPSULE!)

C’est cette capsule qui va être résponsable du mouvement.

2/Mouvements: code (55 min)

On commence par créer toutes les variables necessaires et coder la base: que le personnage se tourne avec la caméra. On en profite aussi pour vérouiller le curseur dans le jeu, et récuperer le rigidbody et gerer le lancement des animations dans Update

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class PersoBouge : MonoBehaviour
{
    public float vitesse;
    public float puissanceSaut;
public int nombre_sauts;
    public float friction;
public Animator mon_animator;
    int nb_saut;
    Vector3 direction;
    Rigidbody rb;
bool auSol;

 

    void Start()
    {
        Cursor.lockState = CursorLockMode.Locked; //verouille le curseur dans le jeu
        rb = GetComponent<Rigidbody>();
    }

 

    void TourneAvecCamera() {
        //recupere la direction de la camera
        Vector3 targetForward = Camera.main.transform.forward;
    //retire les axes X et Z pour que le joueur tourne uniquement sur les coté
        targetForward.y = 0f;
        //applique sur le joueur
        transform.forward = targetForward.normalized;
    }
 
    void Update()
    {
        TourneAvecCamera();
        //on anime la marche
        mon_animator.SetFloat(« vitesse », rb.velocity.magnitude);
mon_animator.SetBool(« air », !auSol);
    }
}

 

Ensuite on va récupere dans Update les inputs du joueur:

    void Update()
    {
        TourneAvecCamera();
        //on recupère les input du joueur
        direction = transform.forward * Input.GetAxisRaw(« Vertical ») + transform.right * Input.GetAxisRaw(« Horizontal »);
        if (Input.GetButtonDown(« Jump »)) {
          //ici on va mettre la fonction du saut
        }
        //on anime la marche
        mon_animator.SetFloat(« vitesse », rb.velocity.magnitude);
mon_animator.SetBool(« air », !auSol);
    }
 
}

On va alors créer une fonction chargée des déplacements, et une fonction qui gère le saut.

    void Bouger() {
        if (auSol) {
            //au sol on avance que si on appuie les touches
            rb.AddForce(direction.normalized * vitesse * 10f, ForceMode.Force);
        }
        else {
            //dans les air, on garde notre élan
            rb.AddForce(direction.normalized * vitesse, ForceMode.Force);
        }
    }
    void Sauter() {
            if (nb_saut > 0) {
            //on reset la vitesse vertical (pour empecher la vitesse de chute d’influencer la puissance d’un saut)
                rb.velocity = new Vector3(rb.velocity.x, 0f, rb.velocity.z);
            //on ajoute la puissance de saut
                rb.AddForce(transform.up * puissanceSaut, ForceMode.Impulse);
            //on décompte le multi-saut
                nb_saut -= 1;
            //on anime le saut
            mon_animator.SetTrigger(« saut »);
            }
    }
}

Enfin, on fait un check pour savoir si on est au sol ou pas:

    private void OnCollisionEnter(Collision collision) {
        if (collision.transform.tag == « sol ») {
            auSol = true;
            nb_saut = nombre_sauts;
        }
    }
    private void OnCollisionExit(Collision collision) {
        if (collision.transform.tag == « sol ») {
            auSol = false;
        }
  }

Dernière étape: on applique nos fonctions aux bons endroits.
La fonction bouger va se mettre dans un Update spécial: le FixedUpdate, qui est plus stable pour les actions qui contiennent de la physique, mais moins réactif au niveau des Inputs. (c’est donc pour ça qu’on à les input dans le Update normal)

Le script final est donc:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PersoBouge : MonoBehaviour
{
    public float vitesse;
    public float puissanceSaut;
    public int nombre_sauts;
    public float friction;
    public Animator mon_animator;
    int nb_saut;
    Vector3 direction;
    Rigidbody rb;
    bool auSol;
    void Start()
    {
        Cursor.lockState = CursorLockMode.Locked; //verouille le curseur dans le jeu
        rb = GetComponent<Rigidbody>();
    }
    void TourneAvecCamera() {
        //recupere la direction de la camera
        Vector3 targetForward = Camera.main.transform.forward;
        //retire les axes X et Z pour que le joueur tourne uniquement sur les coté
        targetForward.y = 0f;
        //applique sur le joueur
        transform.forward = targetForward.normalized;
    }
    void Update()
    {
        TourneAvecCamera();
        //on recupère les input du joueur
        direction = transform.forward * Input.GetAxisRaw(« Vertical ») + transform.right * Input.GetAxisRaw(« Horizontal »);
        if (Input.GetButtonDown(« Jump »)) {
            Sauter();
        }
        //on utilise un physics material de type « ice » pour eviter d’accrocher au murs, il faut remettre un peu de friction pour le sol en consequence
        //sinon notre personnage patine comme sur de la glace
        if (auSol) {
            rb.drag = friction;
        }
        else {
            rb.drag = 0f;
        }
        //on anime la marche
        mon_animator.SetFloat(« vitesse », rb.velocity.magnitude);
mon_animator.SetBool(« air », !auSol);
    }
    private void FixedUpdate() {
        Bouger();
    }
    void Bouger() {
        if (auSol) {
            //au sol on avance que si on appuie les touches
            rb.AddForce(direction.normalized * vitesse * 10f, ForceMode.Force);
        }
        else {
            //dans les air, on garde notre élan
            rb.AddForce(direction.normalized * vitesse, ForceMode.Force);
        }
    }
    void Sauter() {
            if (nb_saut > 0) {
            //on reset la vitesse vertical (pour empecher la vitesse de chute d’influencer la puissance d’un saut)
                rb.velocity = new Vector3(rb.velocity.x, 0f, rb.velocity.z);
            //on ajoute la puissance de saut
                rb.AddForce(transform.up * puissanceSaut, ForceMode.Impulse);
            //on décompte le multi-saut
                nb_saut -= 1;
            //on anime le saut
            mon_animator.SetTrigger(« saut »);
            }
    }
    private void OnCollisionEnter(Collision collision) {
        if (collision.transform.tag == « sol ») {
            auSol = true;
            nb_saut = nombre_sauts;
        }
    }
    private void OnCollisionExit(Collision collision) {
        if (collision.transform.tag == « sol ») {
            auSol = false;
        }
    }
}

Segment 5 Camera du TPS (30 min)

1/Installer cinemachine (5 min)

Les makers installent le package Cinemachine dans leur projet. Si besoin on leur remontre ou trouver le package.

2/Paramètres TPS de cinemachine (25 min)

Les makers créer une camera virtuelle cinemachine, et vont y appliquer les paramètres de la camera TPS.

Tu peux retrouver ces paramètres ici:

Ressource Camera

tout en bas de la page

Retour en haut