Présentation
Lors du développement de mon jeu, je suis arrivé au point de créer les premiers PNJ. Et la question s'est posée de savoir comment faire en sorte que le PNJ contourne le mur et non «y pénètre».
En grimpant sur Internet, j'ai trouvé les algorithmes suivants:
- Recherche étendue (BFS, recherche en largeur)
- Algorithme de Dijkstra (Dijkstra)
- Une étoile "A avec un astérisque"
- Recherche par la première meilleure correspondance (Best-First Search)
- IDA (A avec approfondissement itératif)
- Recherche de points de saut
Et j'ai décidé d'essayer d'implémenter mon A * sur une grille 3D voxel.

Exemple de carte de mon jeu Description de l'algorithme
Un * pas à pas examine tous les chemins menant du sommet initial au sommet final, jusqu'à ce qu'il trouve le minimum. Comme tous les algorithmes de recherche informés, il examine d'abord les itinéraires qui «semblent» menant à l'objectif. De l'algorithme gourmand, qui est également l'algorithme de recherche de la première meilleure correspondance, il se distingue par le fait que lors du choix d'un sommet, il prend en compte, entre autres, l'ensemble du chemin parcouru.
Visualisation du travail d'A * sur Wikipédia Implémentation
Puisque l'algorithme a besoin de "nœuds" - des points pour déterminer le chemin, nous écrivons la structure de classe du nœud:
Code de nœud:public enum EMoveAction { walk, jump, fall, swim }; public class PathPoint {
Constructeurs de petite classe: private PathPoint NewPathPoint(Vector3 point, float pathLenghtFromStart, float heuristicEstimatePathLenght, EMoveAction moveAction) { PathPoint a = new PathPoint(); a.point = point; a.pathLenghtFromStart = pathLenghtFromStart; a.heuristicEstimatePathLenght = heuristicEstimatePathLenght; a.moveAction = moveAction; return a; } private PathPoint NewPathPoint(Vector3 point, float pathLenghtFromStart, float heuristicEstimatePathLenght, EMoveAction moveAction, PathPoint ppoint) { PathPoint a = new PathPoint(); a.point = point; a.pathLenghtFromStart = pathLenghtFromStart; a.heuristicEstimatePathLenght = heuristicEstimatePathLenght; a.moveAction = moveAction; a.cameFrom = ppoint; return a; }
Ensuite, la structure des paramètres de recherche de chemin sera utile:
Code des paramètres de recherche de chemin: public struct SPathFinderType {
En outre, "World" est une sorte de classe de base de données pour stocker des informations sur les blocs de carte. Le vôtre peut être mis en œuvre différemment.
Le résultat de la fonction de recherche de chemin pour obtenir l'itinéraire: public List<PathPoint> GetPathToTarget(Vector3 beginPoint, Vector3 targetPoint, World worldData, SPathFinderType pfType) { List<PathPoint> path = new List<PathPoint>();
Closepoint
La fonction ClosePoint dépend uniquement de l'implémentation de la classe World, elle en ajoute tous les chemins possibles à la liste des points ouverts et supprime le point actuel de cette liste (la ferme). Je vais donner un exemple de mon «point de fermeture» dans les quatre premières directions.
Avertissement gros tas de code private List<PathPoint> ClosePoint(int index, List<PathPoint> openPoints, List<PathPoint> closedPoints, World worldData, SPathFinderType pfType, Vector3 targetPoint) { List<PathPoint> newOpenPoints = openPoints; PathPoint lastPoint = openPoints[index];
Optimisation
En divisant simplement le chemin du début au point actuel, nous réduisons le nombre de nœuds de nombreuses fois et le rendons plus gourmand.
return this.heuristicEstimatePathLenght + this.pathLenghtFromStart /2;
Résumé
Avantages:
- Recherche rapide dans les espaces ouverts.
- Polyvalence des algorithmes
Inconvénients:
- Il faut beaucoup de mémoire pour calculer l'itinéraire.
Le vert montre une liste ouverte de nœuds, un chemin rouge vers la cible, des nœuds fermés bleus.
Itinéraires reçus avant optimisation: Itinéraires reçus après optimisation: Littérature
https://tproger.ru/articles/pathfindings/
https://ru.wikipedia.org/wiki/A*