I have all but ditched the update () method for this, and found an easier solution that seems to be working thus far (for the most part):
First, I set the unit in motion towards the targeted world coordinates:
//ATTACK TARGET IS CLICKED
this.input.setHitArea(tileGroup.getChildren()).on('gameobjectdown', function(pointer, gameObject, scene) {
var attackX = pointer.worldX;
var attackY = pointer.worldY;
var thisX = thisUnit.x;
var thisY = thisUnit.y;
//SET UNIT IN WALKING MOTION
thisUnit.motion = "walk";
//SET UNIT'S INITIAL MOTION
var angle = Math.atan2(attackX - thisY, attackY - thisX);
const direction = Math.atan((attackX - thisX) / (attackY- thisY));
const speed2 = attackY >= thisY ? speed : -speed;
var velX = speed2 * Math.sin(direction);
var velY = speed2 * Math.cos(direction);
thisUnit.setVelocityX(velX);
thisUnit.setVelocityY(velY);
thisUnit.velocityX = velX;
thisUnit.velocityY = velY;
});
Second, I wait for the unit to collide with a tile, then path find every time a collision occurs. If no path is found, it redirects unit towards the stored target world coordinates. If the unit gets stuck at a tile for more than 5 seconds, it triggers itself stuck, and waits random time between 1-5 seconds to redirect towards target again in attempt to un-stuck itself:
//DETECT WALKABLE TILE COLLISIONS
this.matter.world.on('collisionstart', function (event, tileGroup, Units) {
var currentTileDepth = tileGroup.gameObject.depth;
var currentUnitId = Units.gameObject.id;
var currentUnit = Units.gameObject;
currentUnit.depth = currentTileDepth+129;
currentUnit.tileX = tileGroup.gameObject.tileX;
currentUnit.tileY = tileGroup.gameObject.tileY;
console.log('COLLISION. TILE DEPTH: '+currentTileDepth);
if (currentUnit.tileX == currentUnit.targetTileX && currentUnit.tileY == currentUnit.targetTileY) {
//REACHED TARGET TILE, STOP UNIT
currentUnit.motion = "idle";
currentUnit.setVelocityX(0);
currentUnit.setVelocityY(0);
currentUnit.update();
console.log("REACHED DESTINATION");
}
if (currentUnit.stuckTrigger == true) {
//UNIT IS STUCK, WAIT RANDOM TIME BETWEEN 1-5 SECONDS FOR IT TO CLEAR TRIGGER
var randomSeconds = Math.floor(Math.random() * 4999) + 1000;
var randomWait = currentUnit.speed * randomSeconds;
setTimeout(function() {
console.log('UNIT STUCK, WAITING FOR REVERSE MOVEMENT. RANDOM WAIT: '+randomWait);
currentUnit.stuckTrigger = false;
}, randomWait);
}else{
if (currentUnit.motion == "walk" && currentUnit.groundType == true) {
//STORE TILE TO CHECK AGAIN
let lastTileX = currentUnit.tileX;
let lastTileY = currentUnit.tileY;
setTimeout(function() {
//UNIT IS STUCK WALKING ON TILE FOR MORE THAN 5 SECONDS, REDIRECT
if (lastTileX == currentUnit.tileX && lastTileY == currentUnit.tileY) {
//UNIT IS ON SAME TILE, SEND OPPOSITE DIRECTION OF TARGET
var attackX = currentUnit.targetX;
var attackY = currentUnit.targetY;
var thisX = currentUnit.x;
var thisY = currentUnit.y;
var speed = currentUnit.speed;
var angle = Math.atan2(attackX - thisY, attackY - thisX);
const direction = Math.atan((attackX - thisX) / (attackY- thisY));
const speed2 = attackY >= thisY ? speed : -speed;
//var randomVelocity = Math.random() * -1;
//REVERSED VELOCITY
var velX = (speed2 * Math.sin(direction)) * -1;
var velY = (speed2 * Math.cos(direction)) * -1;
currentUnit.setVelocityX(velX);
currentUnit.setVelocityY(velY);
currentUnit.velocityX = velX;
currentUnit.velocityY = velY;
currentUnit.stuckTrigger = true;
currentUnit.update();
}
}, 5000);
currentUnit.path = [];
easystar.findPath(currentUnit.tileX, currentUnit.tileY, currentUnit.targetTileX, currentUnit.targetTileY, function( path ) {
if (path === null) {
//NO PATH FOUND, SEND TOWARDS TARGET AGAIN
console.log("NO PATH, REDIRECT TO TARGET. Current X: "+currentUnit.tileX+' Y: '+currentUnit.tileY+' targetTileX: '+currentUnit.targetTileX+' Y: '+currentUnit.targetTileY);
var attackX = currentUnit.targetX;
var attackY = currentUnit.targetY;
var thisX = currentUnit.x;
var thisY = currentUnit.y;
var speed = currentUnit.speed;
var angle = Math.atan2(attackX - thisY, attackY - thisX);
const direction = Math.atan((attackX - thisX) / (attackY- thisY));
const speed2 = attackY >= thisY ? speed : -speed;
var velX = speed2 * Math.sin(direction);
var velY = speed2 * Math.cos(direction);
currentUnit.setVelocityX(velX);
currentUnit.setVelocityY(velY);
currentUnit.velocityX = velX;
currentUnit.velocityY = velY;
}else{
currentUnit.motion = "walk";
for (var p = 0; p < path.length; p++)
{
currentUnit.path.push(path[p]);
}
//PATH SET UP, MOVE TO FIRST TILE
var thisX = currentUnit.x;
var thisY = currentUnit.y;
var nextTargetX = currentUnit.path[1].x;
var nextTargetY = currentUnit.path[1].y;
var speed = currentUnit.speed;
const tileWidthHalf = tileWidth / 2;
const tileHeightHalf = tileHeight / 2;
const centerX = mapWidth * tileWidthHalf;
const centerY = 64;
//GET WORLD COORDINATES FROM CURRENT TILE
var currentNextPointX = (nextTargetY - nextTargetX) * tileWidthHalf;
var currentNextPointY = (nextTargetY + nextTargetX) * tileHeightHalf;
var currentCoordX = centerX + currentNextPointX;
var currentCoordY = centerY + currentNextPointY;
var angle = Math.atan2(currentCoordX - thisY, currentCoordY - thisX);
const direction = Math.atan((currentCoordX - thisX) / (currentCoordY- thisY));
const speed2 = currentCoordY >= thisY ? speed : -speed;
var velX = speed2 * Math.sin(direction);
var velY = speed2 * Math.cos(direction);
currentUnit.setVelocityX(velX);
currentUnit.setVelocityY(velY);
currentUnit.velocityX = velX;
currentUnit.velocityY = velY;
}
});
easystar.calculate();
console.log('Target Tile X: '+currentUnit.targetTileX+' Y: '+currentUnit.targetTileY);
currentUnit.update();
}
}
});
The only thing I am doing in the update() section is maintaining the unit’s velocity so that it doesnt slow down, like this:
update () {
for (let q in Units) {
//PREVENT SPRITE ROTATION TO PRESERVE ISO
Units[q].body.angle = 0;
if (Units[q].motion == "walk") {
if (Units[q].velocityX !== 'undefined' && Units[q].velocityY !== 'undefined') {
var currentVelX = Units[q].velocityX;
var currentVelY = Units[q].velocityY;
Units[q].setVelocityX(currentVelX);
Units[q].setVelocityY(currentVelY);
}
}
}
My EasyStar config:
//EASYSTAR PATHFINDING
var easystar = new EasyStar.js();
easystar.setGrid(levelData);
easystar.setAcceptableTiles([0, 1, 2, 4, 5, 6, 7, 8]);
(My grass tiles are 1-8)
This seems to work fine in most cases, and it’s the best I’ve got so far, but occasionally the unit gets stuck in corners in strange loops.
What I am wondering is why the unit gets stuck at all… Isn’t the purpose of pathfinding to avoid tiles that will get it stuck in the first place? It’s as if the unit is trying to cut through corners in it’s path, and sometimes it just bounces around in the same tile.
Here it is working as intended:
pathfindingworking.mkv (2.2 MB)
And here it is looping:
pathfindingbadloop.mkv (1.8 MB)
Is there a feature of EasyStar that I can enable to prevent what is happening or am I still missing something entirely?
Also the live demo is at astralforge.net