Rendez vos personnages autonomes avec du pathfinding

navmesh

A partir de l’instant où vous allez ajouter des PNJs (personnage non joueur) dans votre univers, il va falloir leur donner un peu de vie. Si vos PNJs sont de simple marchands plantés derrière un étal sans jamais bouger, cet article ne vous apprendrea rien d’utile. Mais c’est quand même mieux quand ils ont l’air vivants non ?

Déplacer des personnages ce n’est pas très compliqué. Mettre en place une routine de ronde pour les faire se déplacer d’un endroit fixe à un autre (fixe aussi) avec un peu d’aléatoire dedans est relativement simple. Là où ça se complique, c’est lorsque vous voulez amener un personnage à un endroit précis… quel que soit sa position actuelle. Là, pas de doute, il vous faudra utiliser le principe du pathfinding.

Disclaimer

Cet article a été originalement écrit le 28/03/2014 sur mon précédent blog. Certaines informations présentées ici peuvent donc ne plus fonctionner telles quelles.

Le pathfinding c’est quoi ?

Littéralement « recherche de chemin ». Je vais prendre la définition de Wikipédia qui présente très simplement la chose :

La recherche de chemin, couramment appelée pathfinding, est un problème de l’intelligence artificielle qui se rattache plus généralement au domaine de la planification et de la recherche de solution. Il consiste à trouver comment se déplacer dans un environnement entre un point de départ et un point d’arrivée en prenant en compte différentes contraintes.

Prenons un exemple simple, une carte routière (ici Google Maps) :

map

Chaque carrefour est un noeud, les rues sont les liaisons entre les noeuds. Vous pouvez vous déplacer en suivant les rues, de carrefour en carrefour. Pour aller d’un point à un autre, vous allez suivre les rues jusqu’à ce nouveau point. Dans votre tête, vous allez réfléchir à où passer pour aller le plus vite possible et suivre les rues en conséquence. Le pathfinding, c’est le même principe, sauf que c’est un algorithme qui va rechercher le chemin à suivre pour vous (ou plus exactement pour votre PNJ).

Vous conviendrez que ce concept de pathfinding est essentiel à la réalisation d’un jeu comprenant une intelligence articifielle. Bon c’est bien cool tout ça mais comment est-ce qu’on s’y prend pour mettre ça en place dans Unity ?

Mise en place du maillage

Aller, commençons. L’utilisation du pathfinding implique une chose importante : il faut définir le maillage qui sera utilisé lors de la résolution du chemin, et ce, quel que soit l’algorithme de résolution utilisé. Dans Unity, le maillage sera en réalité représenté sous forme de Mesh : un ensemble de faces représentant l’espace « accessible ». Mais pas de panique, c’est très simple à mettre en place et totalement automatique (enfin presque). On appelle ça un NavMesh.

Alors comment générer ce maillage ? Et bien ce n’est pas très compliqué mais il faut suivre… Ce maillage peut être généré à tout moment via la fenêtre Navigation (Window > Navigation).

navigation

Cette fenêtre se découpe en 3 onglets : Object, Bake et Layers. L’onglet Object permet de filter les éléments affichés dans la hiérarchie pour faciliter la création, l’onglet Bake contient les paramètres de génération, et l’onglet Layers permet une utilisation plus avancée basée sur les calques.

Dans Unity, lorsque l’on génère ce maillage, on parle de « bake » (miam !). Si vous essayez de le générer sur une scène vide vous constaterez… qu’il ne se passe rien ! C’est normal. Cette génération se base sur les Meshs de la scène. Pas de mesh : pas de maillage. Ajoutez maintenant un cube sur la scène et générez de nouveau… Il ne se passe rien non plus mais pas pour la même raison. Sélectionnez votre cube et regardez en haut de l’inspecteur. Regardez bien, il y a une petite checkbox « Static » avec un petit menu déroulant tellement discret qu’il faut savoir qu’il existe pour le voir !

static

Vous vous demandiez à quoi elle servait ? Et bien maintenant vous savez : au pathfinding. Le pathfinding dans Unity ne sera généré en prenant en compte que les GameObjects contenant un Mesh dont le GameObject est marqué comme static. Cochez donc cette checkbox et relancez la génération… Rha mais il ne se passe toujours rien, tu te fiches de moi ? Non non, je vous fais volontairement passer par toutes les étapes pour que vous compreniez bien.

Cette fois, tout est correct pour la génération, mais le cube est juste trop petit ! Augmentez un peu son échelle pour en faire un plateau puis retentez la génération. Ah ! Voilà un changement !

navmesh2

Comme sur l’image ci dessus, vous devez voir apparaître au dessus de votre plateau une sorte de quad bleu. Voilà notre fameux NavMesh. Si vous ne voyez rien, augmentez encore l’échelle de votre cube. La zone bleue représente les zones où il est possible d’aller. Vous pouvez modifier les paramètres de génération et tester leurs effets afin d’obtenir le NavMesh qui vous convient.

Avant de continuer, je vous rappelle que le NavMesh est généré à partir de tout GameObject contenant un Mesh et marqué comme « static ». En mettant plusieurs GameObjects sur la scène, on peut générer quelque chose comme ça :

example

Tous les éléments sur la scène sont ici de simples cubes mis à l’échelles et tournés différemments. Vous remarquerez que les pentes sont considérées comme accessibles. Vous pouvez jouer avec l’angle maximum de pente toléré dans l’onglet de génération. N’hésitez pas à jouer avec les paramètres pour voir le résultat !

Utilisation du NavMesh

Bien. Vous savez maintenant comment générer un NavMesh, voyons maintenant comment s’en servir. Ce NavMesh va nous définir un ensemble d’endroits « accessibles ». Il va donc falloir créer une entité pour aller sur ces endroits !

Ajoutez une capsule sur la scène et positionnez la sur le sol que vous avez créé, dans une zone bleue. Il vous faut maintenant ajouter un NavMeshAgent à votre capsule. C’est cet agent qui va s’occuper de la résolution de chemins, à partir du NavMesh de la scène. Il est paramétrable, ce qui influera la résolution, la vitesse de déplacement etc :

agent

Nous avons donc notre agent qui est prêt à calculer des trajets. La suite nécessite un peu de scripting mais rien de complexe. Pour calculer un trajet, notre agent a besoin… d’une destination ! C’est aussi simple que cela. Dès que l’agent recevra une destination, il se déplacera selon le NavMesh pour atteindre ce point (s’il est accessible).

Pour l’exemple, nous allons placer une caméra fixe suivant le mouvement de notre capsule qui se déplacera là où l’on cliquera sur le sol.

public class LookAt : MonoBehaviour
{
    public Transform Target;
 
    void Update ()
    {
        transform.LookAt(Target.position);
    }
}

Affectez ce script à la caméra et affectez la capsule / NavMeshAgent en tant que Target de ce script. Voilà. Si la capsule bouge, la caméra tournera pour regarder dans sa direction. Passons maintenant au plus intéressant : le contrôle de la capsule.

Afin de ne pouvoir cliquer que sur les « dalles » de sol, on va utiliser un Tag particulier : « Walkable ». Créez donc ce nouveau tag et affectez le à tous les éléments utilisés pour la génération du NavMesh. Cette astuce est utile pour une simple et bonne raison : votre scène n’aura pas que des éléments sur lesquels la capsule pourra aller. Par exemple un mur, un arbre, un distributeur de boissons etc… En mettant en place ce tag, nous pourrons savoir si le joueur a cliqué sur le sol ou sur autre chose. Le script maintenant !

public class FollowMouse : MonoBehaviour
{
    private NavMeshAgent _agent;

    void Start ()
    {
        _agent = GetComponent();
    }
 
    void Update ()
    {
        if(Input.GetMouseButton(0))
        {
        // Check for a new destination with raycast
            var ray = Camera.main.ScreenPointToRay(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0));
            var hitInfo = new RaycastHit();
            Physics.Raycast(ray, out hitInfo, 500);
            if(hitInfo.collider.gameObject.tag == "Walkable")
            {
                _agent.SetDestination(hitInfo.point);
            }
        }
    }
}

Dans la méthode Start, on stocke notre agent pour plus tard (et ne pas le récupérer à chaque fois). Dans la méthode Update, on teste si le joueur a cliqué avec le bouton gauche de la souris. A partir de là, nous allons envoyer un rayon depuis la caméra dans la direction où la souris était lorsque le joueur a cliqué. Ici, la distance est arbitrairement 500. Si le rayon touche quelque chose, on vérifie que le GameObject associé a le tag défini plus haut. Si c’est le cas, le point de collision entre le rayon et le GameObject devient la destination de notre agent.

Affectez ce script à la capsule et appréciez le résultat… Lancez le jeu, cliquez quelque part sur les blocs « walkable » et la capsule se dirigera tranquillement vers ce point.

 

Advertisements

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s