Catégories
Autres

Unity3D: Les Stretch Goals du Projet Stealth 3/5

Précédemment sur ce blog :

On continue sur l’IA aujourd’hui, et on va parler comportement de diversion…

Unity Project Stealth
Unity Project Stealth - cliquez pour accéder au tutorial

Exercice 3:Faisons diversion

La fonctionnalité « Attirer l’attention » n’est pas vraiment utile en raison de la vitesse élevée à laquelle les ennemis vont chercher la source sonore. Arrangez-vous pour que lorsque les ennemis entendent du bruit, ils vont chercher la source du bruit en marchant et non en courant.

Pour réussir, vous allez devoir faire attention aux choses suivantes: Est-ce que le EnemyAI script peut être réutilisé tel quel ? Si non, comment peut-on le changer ? Et si l’on créait un nouvel état dans notre machine à états ? A quel priorité doit-il être placé ?

On a déjà plein de fonctionnalités prêtes à l’emploi : nos ennemis sont capables de blender impeccablement entre la marche et la course, ce qui fait qu’on va pouvoir finement régler leur comportement d’investigation. Et on a déjà une méthode pour intercepter les stimulis audios et les investiguer dans EnemySight.cs; sans oublier que le travail effectué dans l’exercice 2 nous permet de gérer simplement la vitesse de l’ennemi.

Une façon rapide d’y parvenir serait de traiter l’investigation comme un sous-état de la poursuite (puisque l’on a une destination autre que le waypoint de base), avec une vitesse et un temps d’attente distincts.

On commence par déclarer nos variables:

[code lang= »csharp »]public class EnemyAI : MonoBehaviour
{
[…]
// ni trop vite, ni trop lentement
public float investigationSpeed = 3.5f;
public float investigationWaitTime = 5f;
[…]
}
[/code]

Puis on va simplement brancher notre comportement dans l’algorithme qui décide si mon IA doit poursuivre ou bien patrouiller:

[code lang= »csharp »]public class EnemyAI : MonoBehaviour
{
[…]
if(enemySight.personalLastSighting != lastPlayerSighting.resetPosition && playerHealth.health > 0f)
{
[…]
MovingImproved (chaseWaitTime, chaseSpeed, enemySight.personalLastSighting);

// tout (trop?) simplement…
if(!enemySight.playerInSight)
MovingImproved (investigationWaitTime, investigationSpeed, enemySight.personalLastSighting);
}
[…]
}
[/code]

Trop simple ? Allons, allons…

On teste, et évidemment ça ne marche pas ! Dès que l’ennemi perd de vue le joueur, il bascule en mode investigation, ce qui n’est pas du tout ce qu’on veut !

Il va donc falloir modifier le comportement de poursuite pour faire marcher notre investigation: L’astuce consiste à empêcher le NPC de quitter son état de poursuite tant qu’il n’aura pas à la fois perdue de vue le héros ET atteint sa dernière position connue.

Pour cela on va ajouter une nouvelle variable qui va nous dire si l’ennemi vu le héros ou non.

[code lang= »csharp »]
private bool seen = false;
[/code]

Si le héros est vu puis disparait, on continue la poursuite jusqu’au Last Known Position. Si le héros n’a pas été vu mais qu’une perturbation audio a été perçue, alors le comportement d’investigation se met en branle.

[code lang= »csharp »]
void Update ()
{
[…]
// If the player has been heard but is not in sight right now and is alive.
if(enemySight.personalLastSighting != lastPlayerSighting.resetPosition && playerHealth.health > 0f)
{

// Ici on lance le mode poursuite si le héros a été vu
if(seen)
MovingImproved (chaseWaitTime, chaseSpeed, enemySight.personalLastSighting);
else
MovingImproved (investigationWaitTime, investigationSpeed, enemySight.personalLastSighting);

}
// If player has not been seen or escaped and is alive
else
{
MovingImproved (patrolWaitTime, patrolSpeed, patrolWayPoints[wayPointIndex].position);
}
}
[/code]

Le booléen seen se met à true dans la fonction Shooting(): si l’ennemi tire, c’est qu’il a vu le héros. Je ne pouvais pas utiliser le bool PlayerInSight puisqu’il passe à false dès que le champ de vision se coupe).

[code lang= »csharp »]
void Shooting ()
{
[…]
seen = true;
}
[/code]

Pour remettre à zéro ce booléen, on va se brancher à l’expiration de la durée d’attente du NPC:

[code lang= »csharp »]
void MovingImproved(float fWaitTime, float fSpeed, Vector3 vDestination)
{
if(nav.destination == lastPlayerSighting.resetPosition || nav.remainingDistance < nav.stoppingDistance)
{
[…]
if(timer >= fWaitTime)
{
[…]
seen = false;
}
}
}
[/code]

Et voilà notre NPC capable d’investiguer correctement !

Bonus !

On termine par quelque chose qui m’a été demandé la dernière fois: faire en sorte que l’ennemi reprenne son point de patrouille le plus proche lorsqu’il quitte son état de poursuite ou d’investigation.

Pour cela on va créer une nouvelle fonction qui va écrire dans la pile de waypoints quel est celui que va aller chercher le NPC.

[code lang= »csharp »]
void SetClosestWaypoint()
{
int minimum = 0;

for(int i=0; i < patrolWayPoints.Length; i++)
{
// On parcoure toute la liste des waypoints, et on stocke celui dont la distance par rapport au NPC est la plus courte
float a = Vector3.Distance(transform.position, patrolWayPoints[i].position);
float b = Vector3.Distance(transform.position, patrolWayPoints[minimum].position);

if (a < b)
minimum = i;
}
// On enregistre le meilleur waypoint
wayPointIndex = minimum;
}
[/code]

Il ne nous reste qu’à brancher la fonction SetClosestWaypoint(), juste au moment où le NPC retourne en patrouille:

[code lang= »csharp »]
void MovingImproved(float fWaitTime, float fSpeed, Vector3 vDestination)
{
if(nav.destination == lastPlayerSighting.resetPosition || nav.remainingDistance < nav.stoppingDistance)
{
[…]

if(timer >= fWaitTime)
{
[…]

if(wayPointIndex == patrolWayPoints.Length – 1)
wayPointIndex = 0;
else
wayPointIndex++;

// If NPCs enters Patrolling state after Chasing or Investigating
if (fWaitTime == chaseWaitTime || fWaitTime == investigationWaitTime)
SetClosestWaypoint();

timer = 0f;
[…]
}
}
}
[/code]

Ceci conclut le troisième exercice. A bientôt pour la suite !

2 réponses sur « Unity3D: Les Stretch Goals du Projet Stealth 3/5 »

Répondre à channie Annuler la réponse

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