Sereen Taleb
Sereen Taleb

Reputation: 79

Ghost AI not moving through intersections (Phaser 3, PacMan)

I am trying to make a pac_man game with Phaser 3. I have implemented the pac_man movement, however, I really need help with the ghost AI. The ghost is moving by its self, however, it is not passing through any intersections. Like, it only moves in a different direction when it collides with something else. I am trying to fix this, and yet I don't know how. I am using physics, with arcade. Here is some of the code:

newDirections(sprite) {
        let directions = ['left', 'right', 'down', 'up']
        return directions.filter(d => !sprite.body.blocked[d]);
}

This function is supposed to see any other paths the ghost can take. I am highly depended on sprite.body.blocked for this (especially since I am using tiles), but it only reads when the sprite is colliding with something else. The problem is, the sprite.body.blocked only returns the accurate dictionary of of blocked areas on a sprite when the sprite is colliding with something. Otherwise, it only returns {none: true, up: false, down: false, left: false, right: false}. Because of this, the ghost, 'sprite' only moves when it collides with something. Thus, it doesn't pass through any intersections.

So basically, I want something that will actively say whether or not it is blocked (touching) by an object or not, not only when it is colliding with an object.

I heavily appreciate any help or advice you'd give.

Upvotes: 1

Views: 88

Answers (1)

winner_joiner
winner_joiner

Reputation: 14795

Well it depends on how you are moving the ghost, but an option would be to add a collider, that checks for overlaps (this.physics.add.overlap(...)) and just querying the tiles around of the current tile the ghost is on (layer.getTileAt(...)), if movement is possible.

Short Demo (Updated):
(ShowCasing movement, just demo code will need tweaking)

Update Demo incorporates the random new direction selection, with special intersection tiles

Letters in SquareBrackes show possible direction change options.

const speed = 10;

// Map with only 2 Tiles
// 1 ... wall
// 0 ... walkable tiles
// 2 ... walkable tile, invisible marker to turn
const level = [
  [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
  [1, 2, 0, 2, 0, 0, 2, 0, 2, 1],
  [1, 0, 1, 0, 1, 1, 0, 1, 0, 1],
  [1, 2, 0, 2, 0, 0, 2, 0, 2, 1],
  [1, 0, 1, 0, 1, 1, 0, 1, 0, 1],
  [1, 2, 0, 2, 0, 0, 2, 0, 2, 1],
  [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
];

// demo helper variables
// needs to be improved
let lastDirection = 'down';

// quickfix
// prevent double direction switch
let lastTile;

class DemoScene extends Phaser.Scene {
  create() {
    this.createDemoAssets();
    this.info = this.add.text(2, 2, '[]').setOrigin(0).setDepth(100).setSize(1);

    const map = this.make.tilemap({ data: level, tileWidth: 8, tileHeight: 8, width: 10, height: 8 });
    map.setCollision([1]);
    const tiles = map.addTilesetImage('tiles', 'tiles', 8, 8);
    const layer = map.createLayer(0, tiles, 0, 0);

    let ghost = this.physics.add.sprite(12, 12, 'ghost').setOrigin(0.5); // set Sprite Origin to center

    ghost.setVelocityY(speed);

    // bind ghost to scene
    this.ghost = ghost;

    console.info(ghost.body.velocity.y * -1);
    this.physics.add.collider(ghost, layer);
    this.physics.add.overlap(ghost, layer, (ghost, tile) => {
      // quick fix double direction switch
      if (lastTile == tile) {
        return;
      }
      //reset label
      this.info.setText(`[]`);

      // only check on special intersection tiles
      if (tile.index == 2) {
        
        let directions = [
          { x: 0, y: -1, name: 'up' },
          { x: 1, y: 0, name: 'right' },
          { x: 0, y: 1, name: 'down' },
          { x: -1, y: 0, name: 'left' },
        ]
          // selects all nodes that are "walkable" "index == 0"
          .filter((node) => {
            return layer.getTileAt(tile.x + node.x, tile.y + node.y).index == 0;
          })
          // creates a new array, with only the name
          .map((x) => x.name);

        console.info(directions);
        this.info.setText(`[${directions.map((x) => x[0]).join(',')}]`);

        // +4 is half tile width == middle of the tile (quickfix for demo)
        if (Phaser.Math.Distance.Between(tile.pixelX + 4, tile.pixelY + 4, ghost.x, ghost.y) < 1) {
           lastTile = tile;
          // select new direction an set speed
          this.setGhostdirection(directions);
        }
      }
    });
  }

  // from here helper functions for demo, should be improved/removed
  createDemoAssets() {
    let graphics = this.make.graphics();

    // tilesheet
    graphics.fillStyle(0xcdcdcd);
    graphics.fillRect(0, 0, 8, 8);
    graphics.fillStyle(0x7a7a7a);
    graphics.fillRect(8, 0, 8, 8);
    graphics.fillStyle(0x00ff00);
    graphics.fillRect(16, 0, 8, 8);
    graphics.generateTexture(`tiles`, 24, 8);

    //ghost
    graphics.fillStyle(0xff0000);
    graphics.fillRect(0, 0, 8, 8);
    graphics.generateTexture(`ghost`, 5, 5);
  }

  setGhostdirection(options) {
    // don't turn 180 degrees
    let goodOptions = options.filter((option) => option != lastDirection);

    let selectedOption = Phaser.Math.RND.pick(goodOptions);

    console.info(selectedOption);
    // set lastDirection
    lastDirection = selectedOption;
    // ghost speed
    switch (selectedOption) {
      case 'down':
        this.ghost.setVelocity(0, speed);
        break;
      case 'up':
        this.ghost.setVelocity(0, -speed);
        break;

      case 'left':
        this.ghost.setVelocity(-speed, 0);
        break;
      case 'right':
        this.ghost.setVelocity(speed, 0);
        break;
    }
  }
}

var config = {
  width: 10 * 8,
  height: 8 * 8,
  zoom: 4,
  physics: {
    default: 'arcade',
  },
  scene: DemoScene,
};

new Phaser.Game(config);

console.clear();
document.body.style = 'margin:0;';
<script src="//cdn.jsdelivr.net/npm/phaser/dist/phaser.min.js"></script>

Info: This is just demo code, it has a few quick fixes, just to make it run smooth for the demo (and to keep the code short), should be improved/optimized for "production".

Upvotes: 2

Related Questions