Depuis Unity 5, implémenter un algorithme de recherche de plus court chemin n’est plus nécessaire grâce à l’ajout des composantes NavMesh. Ces composantes permettent de configurer notre agent afin qu’il trouve le meilleur chemin de sa position à une destination donnée.

Quatre de ces composantes (NavMeshSurface, NavMeshModifier, NavMeshModifierVolume et NavMeshLink) ne sont pas inclues dans l’installateur Unity3D mais vous pouvez les trouver sur le GitHub officiel d’Unity.

L’utilisation de ces composantes pour de la recherche de chemin repose sur la structure de donnée mesh de navigation. Cette méthode est répandue dans l’industrie du jeu vidéo. Elle permet de trouver le chemin le plus court réel (pas celui d’un graphe), réduit le temps de calcul et l’empreinte mémoire dans les environnements ouverts. Elle permet de gérer plus facilement les différents gabarits des agents et les obstacles dynamiques.

Nous allons aborder quelques bases pour l’utilisation des composantes NavMesh dynamiquement.

Vue d’ensemble:

Nous allons commencer par une présentation rapide des deux composantes essentielles à l’utilisation des composantes de navigation.

NavMeshSurface:

C’est cette composante qui définit les endroits accessibles et inaccessibles dans notre environnement. Elle est essentielle car sans cela l’agent ne peut savoir par où passer. L’environnement se configure très facilement depuis l’éditeur grâce à cette composante. Pour ce faire, il suffit d’appuyer sur le bouton Bake après avoir configuré les paramètres comme souhaité. Les paramètres sont expliqués dans la documentation. Je tiens tout de même à parler de 2 de ses paramètres:

  • Collect Objects: Ce paramètre permet de choisir les GameObjects à utiliser pour configurer l’environnement. On peut notamment lui attribuer la valeur Children qui permet de n’utiliser que les GameObject étant enfant du GameObject portant la composante
  • Include Layers: Ce paramètre permet de choisir les couches dans lesquelles on va choisir les GameObject pour la configuration.

L’association de ces deux paramètres permet une configuration précise de l’environnement.

NavMeshAgent:

Cette composante va permettre à l’agent de se déplacer. Un agent ne peut se déplacer si l’environnement n’a pas été configuré grâce à la composante NavMeshSurface. Elle possède différents paramètres définis dans la documentation et est facilement manipulable à travers des scripts.

 

NavMeshModifier:

Cette composante permet d’ajuster le comportement d’un agent. Imaginons que vous souhaitiez configurer la navigation d’un ennemi et qu’il y a des zones que vous souhaitez qu’ils évitent mais peuvent utiliser en dernier recours. Cette composante peut permettre de définir la zone à éviter, l’agent l’évitera donc sauf s’il n’a pas le choix. Pour en savoir plus sur la composante et ses paramètres vous pouvez vous référer à la documentation. Cette composante se base sur la hiérarchie des composantes Transform.

NavMeshModifierVolume:

Cette composante a le même but que celle du dessus sauf qu’elle se base sur le volume. Vous pouvez vous référer à sa documentation pour une étude plus approfondie.

NavMeshLink:

Cette composante permet de lier deux endroits qui utilisent la composante NavMeshSurface. Elle permet de lier différentes surfaces ou une surface à elle-même. Comme montré dans la documentation, elle est utile pour l’utilisation de sauts par exemple.

Utilisation dynamique:

Imaginons que nous souhaitions utiliser ces composantes sur un environnement généré dynamiquement. Le problème survient au niveau de la composante NavMeshSurface car nous devons configurer l’environnement à travers un script. Heureusement nous avons accès à la méthode BuildNavMesh() qui appartient à la composante NavMeshSurface. Cette méthode reproduit le comportement du bouton Bake.

Après différents tests, j’en ai conclu que :

  • Le script interagissant avec la composante NavMeshAgent doit être lancé après que l’environnement soit prêt, c’est-à-dire après que la méthode BuildNavMesh() est été utilisée. Je vous conseille de créer votre propre méthode pour remplacer la méthode standard Start() dans le script de votre agent. En effet lorsqu’on instancie un GameObject, s’il a un script attaché, la méthode standard Start(), usuellement utilisée pour initialiser le script, va être appelée. Voici un exemple de substitution:
  • L’environnement peut être configuré dynamiquement qu’une seule fois par scène. C’est-à-dire que la méthode BuildNavMesh() ne sera ré-utilisable seulement s’il y a un changement de scène.

Pour illustrer le tout, j’ai réalisé un projet mettant en scène l’utilisation des composantes NavMesh dynamiquement. J’utilise uniquement les composantes NavMeshSurface et NavMeshAgent. Mon environnement est créé sous un GameObject parent qui possède le NavMeshSurface. Ci-dessous la configuration de mon NavMeshSurface et de mon NavMeshAgent:

Mon script s’exécute comme suit:

  1. Je génère mon environnement. Pour ma part, c’est un labyrinthe, j’instancie donc le sol et le murs.
  2. Mon algorithme de génération aléatoire de labyrinthe me permet de connaître la position de départ et la position d’arrivée. J’instancie donc l’agent à la position de départ et le portail à la position d’arrivée. L’agent est instancié mais aucun code ne s’exécute car, comme dis ci-dessus, je n’ai pas utilisé la méthode standard Start().
  3. Je fais appelle à la méthode BuildNavMesh() équivalente à Bake.
  4. J’appelle ma méthode remplaçant la méthode standard Start() qui me permet entre autres d’assigner des valeurs à différents attributs dont l’attribut de type NavMeshAgent.
  5. Enfin j’indique la position d’arrivée à l’agent grâce à la méthode SetDestination() définie dans la composante NavMeshAgent
  6. Lorsque l’agent atteint l’arrivée, je relance la scène au lieu de reconstruire l’environnement afin de pouvoir réutiliser la méthode BuildNavMesh(). Si je ne relance pas la scène, malgré que l’environnement soit différent, il gardera la configuration précédente.

Voici les lignes importantes et l’ordre dans lesquels elles doivent être exécutées

Si vous souhaitez voir le code source du projet dans son intégralité vous pouvez regarder mon dépôt GitHub.

Voici le résultat: