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!
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();
}