I'm making a 2D, oblique-styled RPG game and am implementing A* pathfinding for NPCs. The game is on canvas, using melonJS engine and sprites not confined to a 'grid'. I'm having a hard time converting my path (array of x,y coordinates) to my game world x,y coordinates for the sprite.
My game world is a 100 x 100 Tiled tilemap of 64x64px tiles.
I'm using Qiao's A*, which has me create a grid of 0's (walkable) and 1's (unwalkable):
var matrix = [
[0, 0, 0, 1, 0],
[1, 0, 0, 0, 1],
[0, 0, 1, 0, 0],
];
var grid = new PF.Grid(matrix);
Then create the path based on a starting position of 1, 2 (x,y) and ending position of 4, 2 (x,y):
var finder = new PF.AStarFinder();
var path = finder.findPath(1, 2, 4, 2, grid);
Which returns an array of coordinates:
[ [ 1, 2 ], [ 1, 1 ], [ 2, 1 ], [ 3, 1 ], [ 3, 2 ], [ 4, 2 ] ]
Obviously, since my game world consists of 100x100 64x64px tiles, the NPC's coordinates might be something like (1000, 1500). So my thought was to
- get the start world coordinates of the NPC (let's say 1000, 1500),
- divide by 64 to account for px width of grid,
- choose a end location (startX+500, startY+200), divide those by 64 too
when the path is generated for the 100x100 grid, convert the paths back to world coordinates by multiplying each path coordinate x,y by 64
A_Star : function () { var api = {}; api.init = function () { this.grids = this.createGrid(); this.grid = new PF.Grid(this.grids[0]); var finder = new PF.AStarFinder(); var converted_start_x = Math.floor(game.data.NPCX/64); var converted_start_y = Math.floor(game.data.NPCY/64); //arbitrary end location at x+500, y+200 var converted_end_x = Math.floor(500/64); var converted_end_y = Math.floor(200/64); /* thePath returns array of x,y coordinates Passing in grid coordinates for x, y (so grid[1][2]... etc Need to translate the NPC's x,y world coordinates (2000, 1700) to grid coordinates */ var thePath = finder.findPath(converted_start_x, converted_start_y, (converted_start_x+converted_end_y), (converted_start_y+converted_end_y), this.grid); /* Convert pathing back to actual world coordinates (*64) */ for (var i = 0; i < thePath.length; i++) { thePath[i][0] = thePath[i][0] * 64; thePath[i][1] = thePath[i][1] * 64; } game.data.getPath = thePath; game.data.STARTPATH = true; };
Create the grid:
api.createGrid = function () {
var grid = [];
var xLoc = 50;
var yLoc = 50;
for (var y = 0; y < 100; y++) {
grid[y] = [];
nodeGrid[y] = [];
for (var x = 0; x < 100; x++) {
var type = 'walkable';
var val = 0;
//Set an arbitrary wall of unwalkable tiles in grid
if (x === 4 && (y === 3 || y === 4 || y === 5 || y === 6)) {
val = 1; //1 means unwalkable
}
grid[y][x] = val;
xLoc+=64;
}
xLoc = 50;
yLoc += 64;
}
return [
grid,
nodeGrid
];
};
NPC update method: Then, in my NPC class,
- set a counter to keep track of each path coordinate...
- once that path coordinate is reached by the NPC, increment
counter
so the player will start heading to the next path coordinate in the array
...
NPC class {
...
update: function(delta) {
var angle = 0;
/*
Keep loading next set of path coordinates to angle
*/
if (game.data.STARTPATH) {
if (this.counter < game.data.getPath.length) {
angle = this.angleToPoint(new me.Vector2d(game.data.getPath[this.counter][0], game.data.getPath[this.counter][1]));
if (this.pos.x <= game.data.getPath[this.counter][0] && this.pos.y <= game.data.getPath[this.counter][1]) {
this.counter++;
}
} else {
console.log('NPC has reached destination...');
}
this.body.vel.set(Math.cos(-angle) * .9, -Math.sin(-angle) * .9);
this.body.update(delta);
return (this._super(me.Entity, 'update', [delta]) || this.body.vel.x !== 0 || this.body.vel.y !== 0);
}
},
My questions: The pathing seems quite wonky... so I'm obviously doing something wrong.
- Am I correct in dividing by 64px to convert to possible grid coordinates in the 100x100 matrix, getting path of coordinates, then multiplying back by 64px?
- What other recommendations would you have to convert path coordinates then set the path for the player?