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.

