I have a game and I want to be able to have enemies pathfind to the player across a tilemap, is there a way to do this without programming my own a* algorithm? I’ve seen a couple supposedly still updated libraries for this, but neither of which have worked, they have both been broken with npm and not worked when added as a script.
The libraries I’ve tried are Easystar and Navmesh.
Any suggestions would be great, and while Phaser support would be useful, pathfinding over a 2D array would still be a good solution for me. Thanks!
Advertisement
Answer
I never tried it myself (until now), but https://github.com/mikewesthad/navmesh is a great plugin and is very extensive.
… A JS plugin for fast pathfinding using navigation meshes, with optional wrappers for the Phaser v2 and Phaser v3 game engines. …
p.s.: on this page you can find a list of available plugins for phaser https://phaserplugins.com/
Update:
After testing in on an simple an example it worked fine, just be careful not to miss any steps.
Here the steps needed:
get the latest plugin file
phaser-navmesh-plugin.js
(currently from https://github.com/mikewesthad/navmesh/releases/tag/2.1.0)load it from the html file
<script src="phaser-navmesh-plugin.js"></script>
configure the plugin, in the
config
(there are other ways but this is the more convinient)var config = { ... plugins: { scene: [ { key: "PhaserNavMeshPlugin", // Key to store the plugin class under in cache plugin: PhaserNavMeshPlugin, // Class that constructs plugins mapping: "navMeshPlugin", // Property mapping to use for the scene, e.g. this.navMeshPlugin start: true } ] }, scene: { ... } };
setup the map and the layer which should collide:
var map = this.make.tilemap({ data: [[0,1,0],[0,1,0],[0,1,0],[0,0,0]], tileWidth: 8, tileHeight: 8 }); var tiles = map.addTilesetImage('tile01'); var layer = map.createLayer(0, tiles, 0, 0); // this is important, can also be multiple indices layer.setCollision(1);
create navMesh, pass the map and the created layer with should have to collisions.
const navMesh = this.navMeshPlugin.buildMeshFromTilemap("mesh", map, [layer]);
when needed execute pathfinding (x/y positions are in pixels, not tilesId):
const path = navMesh.findPath({ x: 4, y: 4 }, { x: 17, y: 4 });
You can load the plugin in also in the
preload
function like this
this.load.scenePlugin({ key: 'PhaserNavMeshPlugin', url: PhaserNavMeshPlugin, sceneKey: 'navMeshPlugin' });
Here the demo code with a mini path trace when done:
var config = { scene: { preload, create, } }; var game = new Phaser.Game(config); function preload() { this.load.image('tile01', 'tile01.png'); this.load.scenePlugin({ key: 'PhaserNavMeshPlugin', url: PhaserNavMeshPlugin, sceneKey: 'navMeshPlugin' }); } function create() { var map = this.make.tilemap({ data: [[0,1,0],[0,1,0],[0,1,0],[0,0,0]], tileWidth: 8, tileHeight: 8 }); var tiles = map.addTilesetImage('tile01'); var layer = map.createLayer(0, tiles, 0, 0); layer.setCollision(1); const navMesh = this.navMeshPlugin.buildMeshFromTilemap("mesh", map, [layer]); const path = navMesh.findPath({ x: 4, y: 4 }, { x: 17, y: 4 }); if (!path) { return; } let graphics = this.add.graphics(); graphics.lineStyle(2, 0xff0000, 1); graphics.beginPath(); graphics.moveTo(path[0].x, path[0].y); for (let idx = 1; idx < path.length; idx++) { graphics.lineTo(path[idx].x, path[idx].y); } graphics.strokePath(); }