Search code examples
javascriptphaser-frameworkphaserjs

How to make a group of sprites enemy behaviour where they will run at player when within 400 pixels. I have code but am getting errors


How can I make my group follow the player when within 400 pixels? I created code based on this tutorial (https://docs.idew.org/video-game/project-references/phaser-coding/enemy-behavior). My current code I have created is below,

    //IN CREATE FUNCTION
    Map.forEachTile(tile => {
        //Generate sprites on specific map tiles
        if (tile.index == 6) {
            this.Demons.create(tile.pixelX + 8, tile.pixelY + 8, "demon")
                .play('DemonStand')
                .setSize(22, 30)
                .setOffset(5, 7)
                .setDepth(10);
            this.physics.add.collider(this.Demons);
            this.physics.add.collider(this.Demons, MapLayer);
            MonsterCount++;
        }
    });
    //IN UPDATE FUNCTION
    PlayerX = Player.body.position.x;
    PlayerY = Player.body.position.y;
    //PLAYER MOVEMENT || PLAYER MOVEMENT || PLAYER MOVEMENT
this.Demons.forEachAlive(function (enemy) {
  MonsterX = enemy.body.position.x;
      MonsterY = enemy.body.position.y;
  let DifferenceX=PlayerX-MonsterX;
  let DifferenceY=PlayerY-MonsterY;
  if (DifferenceX>=400 || DifferenceX<=400){
    if (MonsterX<PlayerX){
      enemy.body.setVelocityX(EnemySpeed);
      enemy.setScale(1);
      enemy.play("walk", true);
    } else if (MonsterX>PlayerX) {
      enemy.body.setVelocityX(-EnemySpeed);
      enemy.setScale(-1,1);
      enemy.play("walk", true);
    } else {
      enemy.body.setVelocityX(0);
    }
  }
  if (DifferenceY>=400 || DifferenceY<=400){
    if (MonsterY<PlayerY){
      enemy.body.setVelocityY(EnemySpeed);
            enemy.play("walk", true);
    } else if (MonsterY>PlayerY) {
      enemy.body.setVelocityY(-EnemySpeed);
            enemy.play("walk", true);
    } else {
      enemy.body.setVelocityY(0);
    }
  }
});

My demon sprite is working correctly, but I just need to figure out the movement. Is their a better way to implement the follow like this.physics.moveToObject or another way I do not know?

MY FULL CODE (https://replit.com/@JackF99/test-2d-array#script.js),

//CREATE GAME SCENE || CREATE GAME SCENE || CREATE GAME SCENE
class GameScene extends Phaser.Scene {
    constructor(config) {
        super(config);
    }
    preload() {
        //PRELOADING ASSETS || PRELOADING ASSETS || PRELOADING ASSETS
        //SPRITES
        this.load.spritesheet("player", "assets/sprites/player.png", {
            frameWidth: 16,
            frameHeight: 30
        });
        this.load.spritesheet("demon", "assets/sprites/demon.png", {
            frameWidth: 32,
            frameHeight: 36
        });
        this.load.spritesheet("spikes", "assets/sprites/spikes.png", {
            frameWidth: MapTileWidth,
            frameHeight: MapTileHeight
        });
        //TILES
        this.load.image("floor", "assets/tiles/floor.png");
        this.load.image("floor_1", "assets/tiles/floor_1.png");
        this.load.image("floor_2", "assets/tiles/floor_2.png");
        this.load.image("floor_3", "assets/tiles/floor_3.png");
        this.load.image("floor_4", "assets/tiles/floor_4.png");
        this.load.image("floor_5", "assets/tiles/floor_5.png");
        this.load.image("floor_6", "assets/tiles/floor_6.png");
        this.load.image("floor_7", "assets/tiles/floor_7.png");
        this.load.image("floor_8", "assets/tiles/floor_8.png");
        this.load.image("wallLeft", "assets/tiles/wallLeft.png");
        this.load.image("wallRight", "assets/tiles/wallRight.png");
        this.load.image("wallBottom", "assets/tiles/wallBottom.png");
        this.load.image("wallTop", "assets/tiles/wallTop.png");
        this.load.image("bg", "assets/tiles/bg.png");
        //OTHER IMAGES
        this.load.image("sword", "assets/images/sword.png");
        //DECLARE KEYS USED
        this.keys = this.input.keyboard.addKeys('SPACE,W,A,S,D,Q');
        this.cursors = this.input.keyboard.createCursorKeys();
    }
    create() {

        this.cameras.main.zoom = 5;
        //CREATE ANIMATIONS || CREATE ANIMATIONS || CREATE ANIMATIONS
        //PLAYER ANIMATIONS
        this.anims.create({
            key: 'stand',
            frames: this.anims.generateFrameNumbers('player', {
                frames: [0, 1, 2, 3]
            }),
            frameRate: 1,
            repeat: -1
        });
        this.anims.create({
            key: 'walk',
            frames: this.anims.generateFrameNumbers('player', {
                frames: [4, 5, 6, 7]
            }),
            frameRate: 10,
            repeat: -1
        });
        //ENEMY ANIMATIONS
        this.anims.create({
            key: 'DemonStand',
            frames: this.anims.generateFrameNumbers('demon', {
                frames: [0, 1, 2, 3]
            }),
            frameRate: 1.5,
            repeat: -1
        });
        this.anims.create({
            key: 'DemonWalk',
            frames: this.anims.generateFrameNumbers('demon', {
                frames: [4, 5, 6, 7]
            }),
            frameRate: 10,
            repeat: -1
        });
        //GAME OBJECT ANIMATIONS
        this.anims.create({
            key: 'spikes',
            frames: this.anims.generateFrameNumbers('spikes', {
                frames: [0, 1, 2, 3]
            }),
            frameRate: 2,
            repeat: -1
        });
        //DECLARE GROUPS || DECLARE GROUPS || DECLARE GROUPS
        this.Spikes = this.physics.add.staticGroup();
        this.Demons = this.physics.add.staticGroup();
        //GENERATE MAP || GENERATE MAP || GENERATE MAP
        MapOutline = getMap();
        Map = this.make.tilemap({
            data: MapOutline,
            tileWidth: MapTileWidth,
            tileHeight: MapTileHeight
        });
        Map.addTilesetImage(0, 'bg', MapTileWidth, MapTileHeight, 0, 0, 0);
        Map.addTilesetImage(1, 'floor', MapTileWidth, MapTileHeight, 0, 0, 1);
        Map.addTilesetImage(2, 'wallLeft', MapTileWidth, MapTileHeight, 0, 0, 2);
        Map.addTilesetImage(3, 'wallRight', MapTileWidth, MapTileHeight, 0, 0, 3);
        Map.addTilesetImage(4, 'wallBottom', MapTileWidth, MapTileHeight, 0, 0, 4);
        Map.addTilesetImage(5, 'wallTop', MapTileWidth, MapTileHeight, 0, 0, 5);
        Map.addTilesetImage(6, 'floor', MapTileWidth, MapTileHeight, 0, 0, 6); //Monstor Spawn Square
        Map.addTilesetImage(7, 'floor_1', MapTileWidth, MapTileHeight, 0, 0, 7);
        Map.addTilesetImage(8, 'floor_2', MapTileWidth, MapTileHeight, 0, 0, 8);
        Map.addTilesetImage(9, 'floor_3', MapTileWidth, MapTileHeight, 0, 0, 9);
        Map.addTilesetImage(10, 'floor_4', MapTileWidth, MapTileHeight, 0, 0, 10);
        Map.addTilesetImage(11, 'floor_5', MapTileWidth, MapTileHeight, 0, 0, 11);
        Map.addTilesetImage(12, 'floor_6', MapTileWidth, MapTileHeight, 0, 0, 12);
        Map.addTilesetImage(13, 'floor_7', MapTileWidth, MapTileHeight, 0, 0, 13);
        Map.addTilesetImage(14, 'floor_8', MapTileWidth, MapTileHeight, 0, 0, 14);
        Map.addTilesetImage(15, 'floor', MapTileWidth, MapTileHeight, 0, 0, 15);

        MapLayer = Map.createLayer(0, Map.tilesets, 0, 0);
        Map.forEachTile(tile => {
            //Generate sprites on specific map tiles
            if (tile.index == 16) {
                this.Spikes.create(tile.pixelX + 8, tile.pixelY + 8, "spikes")
                    .play('spikes')
                    .setDepth(10);
            }
        });
        Map.forEachTile(tile => {
            //Generate sprites on specific map tiles
            if (tile.index == 6) {
                this.Demons.create(tile.pixelX + 8, tile.pixelY + 8, "demon")
                    .play('DemonStand')
                    .setSize(22, 30)
                    .setOffset(5, 7)
                    .setDepth(10);
                this.physics.add.collider(this.Demons);
                this.physics.add.collider(this.Demons, MapLayer);
                MonsterCount++;
            }
        });
        Map.setCollisionBetween(2, 5, true);
        //CREATE PLAYER || CREATE PLAYER || CREATE PLAYER
        Player = this.add.container(176, 816);
        this.sword = this.add.sprite(4, 8, "sword");
        this.player = this.add.sprite(0, 0, "player");
        Player.setSize(16, 20);
        Player.add([this.sword, this.player]);
        this.physics.add.collider(Player, MapLayer);
        this.cameras.main.startFollow(Player);
        PlayerWeapon = this.add.container(0, 0);
        PlayerWeapon.setSize(22, 22);
        this.physics.add.existing(Player);
        this.physics.add.existing(PlayerWeapon);
        //FINAL CHANGES || FINAL CHANGES || FINAL CHANGES
        this.sword.setOrigin(0.5, 1);
        this.sword.angle = 20;
        MonsterCount--;
        Player.setDepth(100);
        //Damage function
        let CheckSpikeOverlap = this.physics.add.overlap(Player, this.Spikes, (player, spikeSprite) => {
            if (spikeSprite.anims.currentFrame.index == 4) {
                PlayerHealth -= EnemyDamage;
                CheckSpikeOverlap.active = false;
                this.time.addEvent({
                    delay: 1000,
                    callback: () => {
                        CheckSpikeOverlap.active = true;
                    },
                });
            }
        });
        let CheckEnemyOverlap = this.physics.add.overlap(Player, this.Demons, (player, enemy) => {
            // if (enemy.anims.currentFrame.index==4 || enemy.anims.currentFrame.index==8){
            PlayerHealth -= EnemyDamage;
            CheckEnemyOverlap.active = false;
            this.time.addEvent({
                delay: 1000,
                callback: () => {
                    CheckEnemyOverlap.active = true;
                },
            });
            // }
        });
        let CheckWeaponOverlap = this.physics.add.overlap(PlayerWeapon, this.Demons, (weapon, enemy) => {
            if (PlayerAttack == true) {
                enemy.destroy();
                MonsterCount--;
                CheckWeaponOverlap.active = false;
                this.time.addEvent({
                    delay: 1000,
                    callback: () => {
                        CheckWeaponOverlap.active = true;
                    },
                });
            }
        });
    }
    update() {
        PlayerX = Player.body.position.x;
        PlayerY = Player.body.position.y;
        //PLAYER MOVEMENT || PLAYER MOVEMENT || PLAYER MOVEMENT
    this.Demons.forEachAlive(function (enemy) {
      MonsterX = enemy.body.position.x;
          MonsterY = enemy.body.position.y;
      let DifferenceX=PlayerX-MonsterX;
      let DifferenceY=PlayerY-MonsterY;
      if (DifferenceX>=400 || DifferenceX<=400){
        if (MonsterX<PlayerX){
          enemy.body.setVelocityX(EnemySpeed);
          enemy.setScale(1);
          enemy.play("walk", true);
        } else if (MonsterX>PlayerX) {
          enemy.body.setVelocityX(-EnemySpeed);
          enemy.setScale(-1,1);
          enemy.play("walk", true);
        } else {
          enemy.body.setVelocityX(0);
        }
      }
      if (DifferenceY>=400 || DifferenceY<=400){
        if (MonsterY<PlayerY){
          enemy.body.setVelocityY(EnemySpeed);
                enemy.play("walk", true);
        } else if (MonsterY>PlayerY) {
          enemy.body.setVelocityY(-EnemySpeed);
                enemy.play("walk", true);
        } else {
          enemy.body.setVelocityY(0);
        }
      }
    });
        //PLAYER ATTACK
        if (this.keys.SPACE.isDown && PlayerAttackTimer == false) {
            PlayerAttack = true;
        }
        if (PlayerAttack == true && PlayerAttackTimer == false) {
            this.time.addEvent({
                delay: 3,
                callback: () => {
                    this.sword.angle += 2;
                },
                repeat: 35
            });
            this.time.addEvent({
                delay: 295,
                callback: () => {
                    this.time.addEvent({
                        delay: 3,
                        callback: () => {
                            this.sword.angle -= 2;
                        },
                        repeat: 35
                    });
                },
            });
            PlayerAttackTimer = true;
            this.time.addEvent({
                delay: 400,
                callback: () => {
                    PlayerAttack = false;
                },
            });
            this.time.addEvent({
                delay: 1500,
                callback: () => {
                    PlayerAttackTimer = false;
                },
            });
        }
        //MOVE PLAYER WITH KEY PRESS
        if (PlayerFlip == true) {
            Player.setSize(-16, 15);
            PlayerWeapon.x = PlayerX - 6;
            PlayerWeapon.y = PlayerY + 6;
        } else if (PlayerFlip == false) {
            Player.setSize(16, 15);
            PlayerWeapon.x = PlayerX + 23;
            PlayerWeapon.y = PlayerY + 6;
        }
        if (this.cursors.left.isDown || this.keys.A.isDown) {
            Player.body.setVelocityY(0);
            Player.body.setVelocityX(-PlayerSpeed);
            Player.setScale(-1, 1);
            this.player.play("walk", true);
            PlayerFlip = true;
        } else if (this.cursors.right.isDown || this.keys.D.isDown) {
            Player.body.setVelocityY(0);
            Player.body.setVelocityX(PlayerSpeed);
            Player.setScale(1);
            this.player.play("walk", true);
            PlayerFlip = false;
        } else if (this.cursors.up.isDown || this.keys.W.isDown) {
            Player.body.setVelocityX(0);
            Player.body.setVelocityY(-PlayerSpeed);
            this.player.play("walk", true);
        } else if (this.cursors.down.isDown || this.keys.S.isDown) {
            Player.body.setVelocityX(0);
            Player.body.setVelocityY(PlayerSpeed);
            this.player.play("walk", true);
        } else {
            this.player.play("stand", true);
            Player.body.setVelocityX(0);
            Player.body.setVelocityY(0);
        }
    }
}

Solution

  • There are several issues here, to look out for:

    1. first the Demon, group cannot be static if you want to set a velocity. You need to change this line of code this.Demons = this.physics.add.staticGroup(); to this this.Demons = this.physics.add.group();

    2. Than function this.Demons.forEachAlive(...); does not exist (on groups), you would have to rewrite it to something like this

       this.Demons.getChildren().forEach(function (enemy) {
           if (!enemy.active ){
               return;
           }
           ...
       });
      
    3. and as mentioned by Sly_cardinal the check is not 100% correct.

    After these changes the game should run.

    Info/btw: Phaser has great utility / helper functions, that can't help with calculations. I would use the function Phaser.Math.Distance.BetweenPoints(...) or Phaser.Math.Distance.Between(...) (link to the documentation) to messaure / check the distance between the player and enemy.

    The distance check could look something like this:
    if(Phaser.Math.Distance.BetweenPoints(Player.body.position, enemy.body.position) < 400 ) { ... }