Créer un FPS #5 – Déplacer l’ennemi et Intelligence Artificielle

 

Dans cette partie, nous allons créer une mini Intelligence Artificielle, pour que le zombie se déplace vers le joueur.

Préparation

Nous allons utiliser NavMeshComponents. Vous pouvez le télécharger sur GitHub.
https://github.com/Unity-Technologies/NavMeshComponents

(Cliquez sur le Bouton vert « Code », puis sur Download Zip)

Dézippez, puis copiez le dossier Assets > NavMeshComponents dans le dossier Assets du projet.

 

Le NavMeshSurface

Dans la fenêtre Hierarchy :

Faites un clique droit, puis Créer Empty.
Renommez le nouveau GameObject en NavMeshSurface.

Faites Add Component pour ajouter le script NavMeshSurface.

Cliquez sur le bouton Bake dans NavMeshSurface. Cela permet de calculer la surface sur laquelle notre Intelligence Artificielle pourra se déplacer.

La surface bleue correspond à la zone où pourra marcher le zombie.

 

Player

Sélectionnez Player > MainCamera dans la fenêtre Hierarchy.

La caméra est un peu haute, on va la baisser :

Position y = 0,8

 

Le Zombie

Sélectionnez le zombie

Reculez un peu le zombie, par exemple :

Position z = 16

 

Puis on va le redimensionner :

Scale  x,y,z = 1,2

 

NavMeshAgent

Faites Add Component pour ajouter NavMeshAgent.

Voici les paramètres pour le composant NavMeshAgent.

Le zombie va se déplacer, il faut donc déterminer sa vitesse de déplacement :

Speed 3,5

Et il faut aussi lui dire à quelle distance du joueur il devra s’arrêter. Plus tard c’est à cette distance qu’il attaquera !

StoppingDistance 1,5

 

Les animations

Ouvrez l’animator du zombie (Double cliquez sur Controller : Zombie):

Depuis la fenêtre Project :
Faites glisser l’animation Z_Run
Renommez-la en Walk

Ajoutez un paramètre de type bool : Walk
Créez la transition entre Stand et Walk
Condition : Walk = true

Créez une transition entre Walk et Damage
Condition : Damage = true

Et entre Walk et Defeated
Condition : Defeated = true

 

IMPORTANT
Pour toutes les transitions, veuillez décocher HasExitTime !

 

MonsterController

 


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.AI;

public class MonsterController : MonoBehaviour
{
    public GameObject player;


    //Agent de Navigation
    NavMeshAgent navMeshAgent;


    //Composants
    Animator animator;
 
    //Actions possibles
 
    //Stand ou Idle = attendre
    const string STAND_STATE = "Stand";
 
    //Reçoit des dommages
    const string TAKE_DAMAGE_STATE = "Damage";
 
    //Est vaincu
    public const string DEFEATED_STATE = "Defeated";

    //Est en train de marcher
    public const string WALK_STATE = "Walk";



    //Mémorise l'action actuelle
    public string currentAction;
 
    private void Awake()
    {
        //Au départ, la créature attend en restant debout
        currentAction = STAND_STATE;
 
        //Référence vers l'Animator
        animator = GetComponent<Animator>();

        //Référence NavMeshAgent
        navMeshAgent = GetComponent<NavMeshAgent>();

        //Référence de Player
        player = FindObjectOfType<PlayerFPS>().gameObject;
    }
 
    private void Update()
    {

        //si la créature est défaite
        //Elle ne peut rien faire d'autres
        if (currentAction == DEFEATED_STATE)
        {
            navMeshAgent.ResetPath();
            return;
        }


        //Si la créature reçoit des dommages:
        //Elle ne peut rien faire d'autres.
        //Cela servira quand on améliorera ce script.
        if (currentAction == TAKE_DAMAGE_STATE)
        {
            navMeshAgent.ResetPath();
            TakingDamage();
            return;
        }

        if (player != null)
        {
            //Est-ce que l'IA se déplace vers le joueur ?
            if (MovingToTarget())
            {
                //En train de marcher
                return;
            }
           
        }
    }
 
    //La créature attend
    private void Stand()
    {
        //Réinitialise les paramètres de l'animator
        ResetAnimation();
        //L'action est maintenant "Stand"
        currentAction = STAND_STATE;
        //Le paramètre "Stand" de l'animator = true
        animator.SetBool("Stand", true);
    }
 
    public void TakeDamage()
    {
        //Réinitialise les paramètres de l'animator
        ResetAnimation();
        //L'action est maintenant "Damage"
        currentAction = TAKE_DAMAGE_STATE;
        //Le paramètre "Damage" de l'animator = true
        animator.SetBool("Damage", true);
    }
 
    public void Defeated()
    {
        //Réinitialise les paramètres de l'animator
        ResetAnimation();
        //L'action est maintenant "Defeated"  
        currentAction = DEFEATED_STATE;
        //Le paramètre "Defeated" de l'animator = true
        animator.SetBool(DEFEATED_STATE, true);
    }
 
 
    //Permet de surveiller l'animation lorsque l'on prend un dégât
    private void TakingDamage()
    {
 
        if (this.animator.GetCurrentAnimatorStateInfo(0).IsName(TAKE_DAMAGE_STATE))
        {
            //Compte le temps de l'animation
            //normalizedTime : temps écoulé nomralisé (de 0 à 1).
            //Si normalizedTime = 0 => C'est le début.
            //Si normalizedTime = 0.5 => C'est la moitié.
            //Si normalizedTime = 1 => C'est la fin.
 
 
            float normalizedTime = this.animator.GetCurrentAnimatorStateInfo(0).normalizedTime;
 
 
            //Fin de l'animation
            if (normalizedTime > 1)
            {
                Stand();
            }
 
        }
 
    }


    private bool MovingToTarget()
    {
        
        //Assigne la destination : le joueur
        navMeshAgent.SetDestination(player.transform.position);

        
        // navMeshAgent.remainingDistance = distance restante pour atteindre la cible (Player)
        // navMeshAgent.stoppingDistance = à quelle distance de la cible l'IA doit s'arrêter 
        // (exemple 2 m pour le corps à sorps) 
        if (navMeshAgent.remainingDistance > navMeshAgent.stoppingDistance)
        {

            if (currentAction != WALK_STATE)
                Walk();

        }
        else
        {
            //Si arrivé à bonne distance, regarde vers le joueur
            RotateToTarget(player.transform);
            return false;
        }

        return true;
    }


    //Walk = Marcher
    private void Walk()
    {
        //Réinitialise les paramètres de l'animator
        ResetAnimation();
        //L'action est maintenant "Walk"
        currentAction = WALK_STATE;
        //Le paramètre "Walk" de l'animator = true
        animator.SetBool(WALK_STATE, true);
    }

    //Permet de tout le temps regarder en direction de la cible
    private void RotateToTarget(Transform target)
    {
        Vector3 direction = (target.position - transform.position).normalized;
        Quaternion lookRotation = Quaternion.LookRotation(new Vector3(direction.x, 0, direction.z));
        transform.rotation = Quaternion.Slerp(transform.rotation, lookRotation, Time.deltaTime * 3f);
    }

    //Réinitialise les paramètres de l'animator
    private void ResetAnimation()
    {
        animator.SetBool(STAND_STATE, false);
        animator.SetBool(TAKE_DAMAGE_STATE, false);
        animator.SetBool(DEFEATED_STATE, false);
        animator.SetBool(WALK_STATE, false);
    }
 
}

Ajouter des obstacles

Maintenant notre zombie, va foncer automatiquement vers le joueur !

Encore plus fort, si vous ajoutez des obstacles sur le sol, le zombie les évitera.

Ajoutez des cubes au hasard sur la scène, et sélectionnez NavMeshSurface.

Cliquez sur le bouton Bake dans NavMeshSurface. Cela permet de calculer la surface sur laquelle notre IA pourra se déplacer.

A chaque fois que la scène est modifiée, il faut recalculer la surface. Comme cela les changements de terrain sont pris en compte.

 

Partie 6 – L’ennemi attaque le joueur

Recevez les dernières actus

Nous ne spammons pas ! Consultez notre politique de confidentialité pour plus d’informations.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Retour en haut