Créer un FPS #6 – L’ennemi attaque le joueur. Tuto Unity en français

Dans cette partie, le Zombie attaque le joueur et fait des dégâts.

Player

Créez un nouveau Layer : Player.
Assignez au GameObject Player, le Layer « Player »
Ajoutez le script ReceiveDamage :
Max Hit Points = 10

Le Zombie

L’arme de l’ennemi

Créez un Cube 3D dans le Zombie.
Renommez-le en « Weapon ».
Scale : x, y et z = 0.3
Dans le component Box-collider, cochez la case Is Trigger.

On veut que l’arme du Zombie se situe au niveau de sa main droite. On va créer un script afin que Weapon suive toujours sa main droite.

Créez un nouveau script : Follow

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Follow : MonoBehaviour
{
    public Transform target;

    
    void Update()
    {
        transform.position = target.position;
        transform.rotation = target.rotation;
    }
}

Dans la recherche de la Hierachy, cherchez :
HumanRArmPalm

Sélectionnez Weapon, dans le composant Follow :
Target = HumanRArmPalm

Créez un nouveau script : MeleeWeapon

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MeleeWeapon : MonoBehaviour
{
    //Damage que fait l'arme
    public int damage = 1;

    //Détermine quel Layer on peut toucher
    public LayerMask layerMask;

    //Est-ce que l'arme est en train d'être utilisée ?
    public bool isAttacking = false;


    public void StartAttack()
    {
        isAttacking = true;
    }

    public void StopAttack()
    {
        isAttacking = false;
    }

    //Quand MeleeWeapon entre en collision avec objet
    private void OnTriggerEnter(Collider other)
    {
        if (!isAttacking)
            return;

        if ((layerMask.value & (1 << other.gameObject.layer)) == 0)
            return;

        //Fait des dommages au GameObject qu'on a touché
        other.GetComponent<ReceiveDamage>().GetDamage(damage);

    }
}

L’animation d’attaque

Sélectionnez le Zombie et dans le composant Animator, double-cliquez dans Controller sur Zombie.
La fenêtre Animator s’ouvre.

Dasn Parameters, cliquez sur  « + » > Bool.
Renommez en « Attack »

Dans Base Layer, faites un clique droit dans une zone vide > Create State > Empty

Sélectionnez New State.
Renommez-le en « Attack ».
Dans Motion, sélectionnez Z_Attack.anim

On va faire maintenant les tranisitions entre les animations, dans Base Layer :

  • Faites un clique droit sur Attack, Make Transition, cliquez sur Stand (bouton gauche de la souris).
    Sélectionnez la transition (la flèche)

    • Décochez Has Exit Time
    • Dans Conditions, ajoutez une condition : Attack = false
  • Faites un clique droit sur Stand, Make Transition, cliquez sur Attack.
    Sélectionnez la transition

    • Décochez Has Exit Time
    • Dans Conditions, ajoutez une condition : Attack = true
  • Faites un clique droit sur Walk, Make Transition, cliquez sur Attack.
    Sélectionnez la transition

    • Décochez Has Exit Time
    • Dans Conditions, ajoutez une condition : Attack = true
  • Faites un clique droit sur Attack, Make Transition, cliquez sur Damage.
    Sélectionnez la transition

    • Décochez Has Exit Time
    • Dans Conditions, ajoutez une condition : Damage = true
  • Faites un clique droit sur Walk, Make Transition, cliquez sur Stand.
    Sélectionnez la transition

    • Décochez Has Exit Time
    • Dans Conditions, ajoutez une condition : Stand = true

 

Modification de MonsterController

J’ai mis en valeur les lignes qui ont été rajoutées.

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

public class MonsterController : MonoBehaviour
{
    public GameObject player;

    public MeleeWeapon meleeWeapon;

    //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";

    //Attaque
    public const string ATTACK_STATE = "Attack";


    //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;
            }
            else
            {
                if (currentAction != ATTACK_STATE && currentAction != TAKE_DAMAGE_STATE)
                {
                    Attack();
                    return;
                }
                if (currentAction == ATTACK_STATE)
                {
                    Attacking();
                    return;
                }

                //Defaut
                Stand();
                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 void Attacking()
    {
        if (this.animator.GetCurrentAnimatorStateInfo(0).IsName(ATTACK_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 % 1;


            //Fin de l'animation
            if (normalizedTime > 1)
            {

                meleeWeapon.StopAttack();
                Stand();
                return;
            }

            meleeWeapon.StartAttack();

        }
    }


    private bool MovingToTarget()
    {

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

        //Si navMeshAgent n'est pas prêt
        if (navMeshAgent.remainingDistance == 0)
            return true;


        // 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);
    }


    private void Attack()
    {
        //Réinitialise les paramètres de l'animator
        ResetAnimation();
        //L'action est maintenant "Attack"
        currentAction = ATTACK_STATE;
        //Le paramètre "Attack" de l'animator = true
        animator.SetBool(ATTACK_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);
        animator.SetBool(ATTACK_STATE, false);
    }

}

Dans Unity, sélectionnez le Zombie, dans MonsterController:
Melee Weapon : assignez Weapon.

Sélectionnez Weapon, désactivez MeshRender, pour que l’arme soit invisible.

Maintenant, le Zombie se déplace vers le joueur, s’arrête à bonne distance et attaque. Le joueur perd des points de vie.

Partie 7

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