Pathfinding using easystar

Hi there. I’ve been toying with easystar, a pathfinding library and I’ve run into a problem. I’ve borrowed a lot of code from this tutorial here, and I can set an entity to move to an x and y position. As in the codepen example, the entity will move to the position.

codepen here

I setup the grid code for easystar in the main game scene. And I have the rest of the code for movement in the entity class. I’m not sure if separating the code was a good idea, but the initial thing seems to work. I’m also using a state machine, which may be overkill but I decided to stick to it for now.

I’ve got the entity moving to its position. I however want the entity to change its path once some input is made. The player does not have direct control of the entity.

class NPC extends Entity {
  constructor(scene, x, y, texture){
    super(scene, x, y, texture)
      this.scene = scene
      this.finder = scene.finder
      this.setOrigin(0.16, 0.5);
      this.setFrame(7)

      this.npcStateMachine = new StateMachine('arrive', {
        arrive: new NPCArriveState(),
        cashRegister: new NPCCashRegisterState(),
      }, [this, scene])
  }
  
  handleMove(a, b) {
    let x = a 
    let y = b
    let tileXY = this.scene.map.tileToWorldXY(x, y)
    let toX = Math.floor(tileXY.x  / 16);
    let toY = Math.floor(tileXY.y  / 16);
    let fromX = Math.floor(this.getEntity().x / 16);
    let fromY = Math.floor(this.getEntity().y / 16);
    console.log('going from (' + fromX + ',' + fromY + ') to (' + toX + ',' + toY + ')');

    let currentThis = this

    this.finder.findPath(fromX, fromY, toX, toY, function (path) {
      if (path === null) {
        console.warn("Path was not found.");
      } else {
        currentThis.moveCharacter(path)
      }
    });
    this.finder.calculate(); // don't forget, otherwise nothing happens
  }
  
  cancelPath(a, b) {
    let x = a
    let y = b
    let tileXY = this.scene.map.tileToWorldXY(x, y)
    let toX = Math.floor(tileXY.x / 16);
    let toY = Math.floor(tileXY.y / 16);
    let fromX = Math.floor(this.getEntity().x / 16);
    let fromY = Math.floor(this.getEntity().y / 16);

    let instanceId = this.finder.findPath(fromX, fromY, toX, toY, () => {});
        // ...
    this.finder.cancelPath(instanceId);
    this.finder.calculate()
  }
  
  moveCharacter(path) {
    let tweens = []
    for (let i = 0; i < path.length - 1; i++) {
      let ex = path[i + 1].x
      let ey = path[i + 1].y
      tweens.push({
        targets: this,
          x: { value: ex * this.scene.map.tileWidth, duration: 200 },
          y: { value: ey * this.scene.map.tileHeight, duration: 200 }
        })
      }
      this.scene.tweens.timeline({
        tweens: tweens
      })
    }
    getEntity() {
      return this
    }
    update() {
      this.npcStateMachine.step()
    }
}

I’ve made a sprite that once clicked, places the sprite on the screen, as in the codepen example. I want the entity to change its path once input is made and the sprite has been placed. Once placed on the map, I want all current and new entities to move to the sprite.

I’ve tried using a built-in function that phaser uses cancelPath() but I don’t know how to use it properly.

The codepen example has some funny behaviour. Once the entity has the second path, the entity moves to the new path but then continues on the previous path. I’d prefer the first path to be removed once a new path is available.

I made a boolean that had been used in the execute function, to trigger when the sprite is placed.

class NPCArriveState extends State {
  enter(entity, scene) {
    console.log("arrive state")
    entity.handleMove(10, 11)
  }
  execute(entity, scene) {

    if (scene.objectEntities.getChildren()[0].hasBeenPlaced == true) {
      console.log("has been placed")
      entity.cancelPath(10, 11)
      console.log(entity.finder)
      // entity.finder.calculate()
      this.stateMachine.transition("cashRegister")
    } else {
      console.log("has not been placed")
    }        
  }
}

class NPCCashRegisterState extends State {
  enter(entity, scene) {
    let instanceID = entity.finder.findPath()
    entity.finder.calculate()
    entity.handleMove(9,9)    
  }
  execute(entity, scene) {
    
  }
}

The first state calls the handleMove() function. Once the condition has been met, it transitions to the next state which also calls the handleMove() function. I suspect this is pretty bad logic. I’m having trouble trying to see any alternatives at this point.

I thought this would be relatively easy. But It turns out it is not. The logic is a bit much for me to understand, and with my lack of understanding makes it even more difficult.

I hope none of this is too confusing. Any suggestions or advice would be much appreciated once again. Thanks

It looks like your code is moving the NPC through each path point using a timeline of tweens. I think what you can do is cancel the current timeline when starting a new one so that the older timeline won’t continue running and move the character after the second timeline is finished.

I have an implementation with EasyStar that doesn’t use tweens here that might be helpful: phaser3-vs-kaboomjs/src/astar/phaser at master · ourcade/phaser3-vs-kaboomjs · GitHub