I want to make npc's (enemies) come and go at regular intervals


I want to walk npc, but I get an error.
How can I achieve this?

const config = {
  type: Phaser.AUTO,
  parent: 'game',
  width: 1500,
  heigth: 660,
  scale: {
    mode: Phaser.Scale.FIT,
    autoCenter: Phaser.Scale.CENTER_BOTH
  },
  scene: {
    preload,
    create,
    update,
  },
  physics: {
    default: 'arcade',
    arcade: {
      gravity: {
        y: 300
      },
      debug: true,
    },
  }
};

const game = new Phaser.Game(config);
var npc;
var num = 0;
var ran = 0;

function getRandam(n, m) {
  for (let i = 0; i < 5; i++) {
    let num = Math.floor(Math.random() * (m + 1 - n)) + n;
    return num;
  }
};

function preload() {
  this.load.image('background', 'assets/images/background.png');
  this.load.image('spike', 'assets/images/spike.png');
  // At last image must be loaded with its JSON
  this.load.atlas('player', 'assets/images/kenney_player.png', 'assets/images/kenney_player_atlas.json');
  this.load.image('tiles', 'assets/tilesets/platformPack_tilesheet.png');
  // Load the export Tiled JSON
  this.load.tilemapTiledJSON('map', 'assets/tilemaps/level1.json');
  this.load.image('star', 'assets/images/star.png');
  this.load.spritesheet('dude', 'assets/images/dude.png', {
    frameWidth: 32,
    frameHeight: 48
  });
}

function create() {
  const backgroundImage = this.add.image(0, 0, 'background').setOrigin(0, 0);
  backgroundImage.setScale(2, 0.8);
  const map = this.make.tilemap({
    key: 'map'
  });
  const tileset = map.addTilesetImage('kenny_simple_platformer', 'tiles');
  const platforms = map.createStaticLayer('Platforms', tileset, 0, 200);
  platforms.setCollisionByExclusion(-1, true);
  this.player = this.physics.add.sprite(50, 300, 'player');
  this.player.setBounce(0.1);
  this.physics.world.setBounds(0, 0, 900 * 2, 380 * 2);
  this.cameras.main.setBounds(0, 0, 900 * 2, 380 * 2);
  this.cameras.main.startFollow(this.player, true, 0.05, 0.05);
  this.player.setCollideWorldBounds(true);
  this.physics.add.collider(this.player, platforms);
  this.anims.create({
    key: 'walk',
    frames: this.anims.generateFrameNames('player', {
      prefix: 'robo_player_',
      start: 2,
      end: 3,
    }),
    frameRate: 10,
    repeat: -1
  });
  this.anims.create({
    key: 'idle',
    frames: [{
      key: 'player',
      frame: 'robo_player_0'
    }],
    frameRate: 10,
  });
  this.anims.create({
    key: 'jump',
    frames: [{
      key: 'player',
      frame: 'robo_player_1'
    }],
    frameRate: 10,
  });
  this.cursors = this.input.keyboard.createCursorKeys();
  this.spikes = this.physics.add.group({
    allowGravity: false,
    immovable: true
  });
  map.getObjectLayer('Spikes').objects.forEach((spike) => {
    const spikeSprite = this.spikes.create(spike.x, spike.y + 200 - spike.height, 'spike').setOrigin(0);
    spikeSprite.body.setSize(spike.width, spike.height - 20).setOffset(0, 20);
  });
  this.physics.add.collider(this.player, this.spikes, playerHit, null, this);

  //Add star
  stars = this.physics.add.group({
    key: 'star',
    setXY: {
      x: 450,
      y: 0,
    }
  });
  stars.children.iterate(function (child) {

    //  Give each star a slightly different bounce
    child.setBounceY(Phaser.Math.FloatBetween(0.4, 0.8));

  });
  this.physics.add.collider(stars, platforms);

  //Add npc
  npc = this.physics.add.group({
    key: 'dude',
    setXY: {
      x: 700,
      y: 20
    }
  });
  //  Our npc animations, turning, walking left and walking right.
  this.anims.create({
    key: 'leftNpc',
    frames: this.anims.generateFrameNumbers('dude', {
      start: 0,
      end: 3
    }),
    frameRate: 10,
    repeat: -1
  });

  this.anims.create({
    key: 'turnNpc',
    frames: [{
      key: 'dude',
      frame: 4
    }],
    frameRate: 20
  });

  this.anims.create({
    key: 'rightNpc',
    frames: this.anims.generateFrameNumbers('dude', {
      start: 5,
      end: 8
    }),
    frameRate: 10,
    repeat: -1
  });
  this.physics.add.collider(npc, platforms);
}

function update() {


  //I want to walk npc
  ran = getRandam(0, 70);
  if (ran == 0) {
    if (num == 0) {
      npc.setVelocityX(-30);
      this.npc.anims('leftNpc', true);
      num = 1;
    } else if (num == 1) {
      npc.setVelocityX(30);
      this.npc.anims('rightNpc', true);
      num = 0;
    }
  }

  //Player processing
  if (this.cursors.left.isDown) {
    this.player.setVelocityX(-200);
    if (this.player.body.onFloor()) {
      this.player.play('walk', true);
    }
  } else if (this.cursors.right.isDown) {
    this.player.setVelocityX(200);
    if (this.player.body.onFloor()) {
      this.player.play('walk', true);
    }
  } else {
    // If no keys are pressed, the player keeps still
    this.player.setVelocityX(0);
    // Only show the idle animation if the player is footed
    // If this is not included, the player would look idle while jumping
    if (this.player.body.onFloor()) {
      this.player.play('idle', true);
    }
  }

  // Player can jump while walking any direction by pressing the space bar
  // or the 'UP' arrow
  if ((this.cursors.space.isDown || this.cursors.up.isDown) && this.player.body.onFloor()) {
    this.player.setVelocityY(-350);
    this.player.play('jump', true);
  }
  if (this.player.body.velocity.x > 0) {
    this.player.setFlipX(false);
  } else if (this.player.body.velocity.x < 0) {
    // otherwise, make them face the other side
    this.player.setFlipX(true);
  }
}

//Collision detection between spikes and players
function playerHit(player, spike) {
  player.setVelocity(0, 0);
  player.setX(50);
  player.setY(0);
  player.play('idle', true);
  player.setAlpha(0);
  let tw = this.tweens.add({
    targets: player,
    alpha: 1,
    duration: 100,
    ease: 'Linear',
    repeat: 5,
  });
}

What is the error?

npc animation doesn’t work

 if (num == 0) {
      npc.setVelocityX(-30);
      this.npc.anims('leftNpc', true);
      num = 1;
    } else if (num == 1) {
      npc.setVelocityX(30);
      this.npc.anims('rightNpc', true);
      num = 0;
    }
}

this.npc is undefined.

Pick either npc (the variable) or this.npc (the object property) and use it only.

Also npc is a group, not a sprite. If you don’t need a group, use a sprite instead. If you do use a group, you need to loop through it.

for (const npcSprite of npc.getChildren()) {
  npcSprite.setVelocityX(-30);
}

Thank you. It’s done.
It was a very good learning experience.
I want to change the direction of the npc at regular intervals, but the current code doesn’t work.
Is there a better way?

var fn = function () {
    ran = getRandam(0, 70);
    if (ran != 0) {
      for (const npcSprite of npc.getChildren()) {
        if (num == 0) {
          npcSprite.setVelocityX(-10);
          npcSprite.play('leftNpc', true);
          num = 1;
        } else if (num == 1) {
          npcSprite.setVelocityX(10);
          npcSprite.play('rightNpc', true);
          num = 0;
        }
      }
    }
  }

  var id = setInterval(fn, 1);

One timer event for each character.


The movement is getting closer to what I think it should be.
However, I would like to keep the distance to go forward and backward constant, is there any way to do that?

timedEvent1 = this.time.addEvent({
    delay: 2000,
    callback: onEvent1,
    callbackScope: this,
    loop: true
  });
  timedEvent2 = this.time.addEvent({
    delay: 3000,
    callback: onEvent2,
    callbackScope: this,
    loop: true
  });
function onEvent1() {
  var num = 0;
  for (const npcSprite of npc.getChildren()) {
    num = 1;
    npcSprite.setVelocityX(40);
    npcSprite.play('rightNpc', true);
  }
}

function onEvent2() {
  var num = 0;
  for (const npcSprite of npc.getChildren()) {
    num = 1;
    npcSprite.setVelocityX(-40);
    npcSprite.play('leftNpc', true);
  }
}

Try

timedEvent1 = this.time.addEvent({
  delay: 4000,
  startAt: 2000,
  callback: onEvent1,
  callbackScope: this,
  loop: true,
});

timedEvent2 = this.time.addEvent({
  delay: 4000,
  callback: onEvent2,
  callbackScope: this,
  loop: true,
});
1 Like

This is exactly the move I was looking for!
Thank you.
I appreciate it very much.

1 Like