3

I have a spike sprite that has 4 frames in its loop. I want to subtract 1 from the health variable when the player overlaps with 3rd frame of the spike sprite.

Currently, the .on function will not load properly. My game works, but the overlap function just wont work at all. I edited the code I recieved and deleted the aspects that I dont think were needed (tested the original code sample to check if it would work, and my changes still worked. Also didnt work with complete code I was sent)

My working code is below,

let CheckSpikeOverlap=this.physics.add.overlap(this.cPlayer, this.spikes, (player, spikeSprite)=> {
       if (spikeSprite.anims.currentFrame.index==4){
          PlayerHealth=PlayerHealth-EnemyDamage;
          CheckSpikeOverlap.active=false;
          this.time.addEvent({
                    delay: 1000,
                    callback: () => {
              CheckSpikeOverlap.active=true;
                    },
                });
       }
     });

My full code (https://replit.com/@JackF99/test-2d-array#script%20(copy).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("sword", "assets/sprites/sword.png", {
            frameWidth: 10,
            frameHeight: 22
        });
        this.load.spritesheet("spikes", "assets/sprites/spikes.png", {
            frameWidth: tileWidth,
            frameHeight: tileHeight
        });
        //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("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");
        //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
        });
        //WEAPON ANIMATIONS
        this.anims.create({
            key: 'sword',
            frames: this.anims.generateFrameNumbers('sword', {
                frames: [6]
            }),
        });
        this.anims.create({
            key: 'attack',
            frames: this.anims.generateFrameNumbers('sword', {
                frames: [0, 1, 2, 3, 4, 5]
            }),
            frameRate: 5
        });
        //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();
        //GENERATE MAP || GENERATE MAP || GENERATE MAP
        var level = getMap();
        let map = this.make.tilemap({
            data: level,
            tileWidth: tileWidth,
            tileHeight: tileHeight
        });
        map.addTilesetImage(0, 'bg', tileWidth, tileHeight, 0, 0, 0);
        map.addTilesetImage(1, 'floor', tileWidth, tileHeight, 0, 0, 1);
        map.addTilesetImage(2, 'wallLeft', tileWidth, tileHeight, 0, 0, 2);
        map.addTilesetImage(3, 'wallRight', tileWidth, tileHeight, 0, 0, 3);
        map.addTilesetImage(4, 'wallBottom', tileWidth, tileHeight, 0, 0, 4);
        map.addTilesetImage(5, 'wallTop', tileWidth, tileHeight, 0, 0, 5);
        map.addTilesetImage(6, 'floor_1', tileWidth, tileHeight, 0, 0, 6);
        map.addTilesetImage(7, 'floor_2', tileWidth, tileHeight, 0, 0, 7);
        map.addTilesetImage(8, 'floor_3', tileWidth, tileHeight, 0, 0, 8);
        map.addTilesetImage(9, 'floor_4', tileWidth, tileHeight, 0, 0, 9);
        map.addTilesetImage(10, 'floor_5', tileWidth, tileHeight, 0, 0, 10);
        map.addTilesetImage(11, 'floor_6', tileWidth, tileHeight, 0, 0, 11);
        map.addTilesetImage(12, 'floor_7', tileWidth, tileHeight, 0, 0, 12);
        map.addTilesetImage(13, 'spikes', tileWidth, tileHeight, 0, 0, 13);
        map.addTilesetImage(14, 'floor', tileWidth, tileHeight, 0, 0, 14);

        let mapLayer = map.createLayer(0, map.tilesets, 0, 0);
        map.forEachTile(tile => {
            //Generate sprites on specific map tiles
            if (tile.index == 13) {
                this.spikes.create(tile.pixelX + 8, tile.pixelY + 8, "spikes")
          .play('spikes')
                    .setDepth(10);
            }
        });
        map.setCollisionBetween(2, 5, true);
        //CREATE PLAYER || CREATE PLAYER || CREATE PLAYER
        this.cPlayer = this.add.container(176, 816);
        this.player = this.add.sprite(0, 0, "player");
        this.sword = this.add.sprite(10, 3, "sword");
        this.cPlayer.setSize(16, 20);
        this.physics.add.existing(this.cPlayer);
        this.cPlayer.add([this.player, this.sword]);
        this.physics.add.collider(this.cPlayer, mapLayer);
        this.cameras.main.startFollow(this.cPlayer);
    this.weapon = this.add.container(0, 0);
    this.weapon.setSize(12, 20);
        //FINAL CHANGES || FINAL CHANGES || FINAL CHANGES
        this.sword.play("sword", true);
        this.cPlayer.setDepth(100);
        this.physics.add.existing(this.cPlayer);
    this.physics.add.existing(this.weapon);
    //Damage function
    this.spikes.on(Phaser.Animations.Events.ANIMATION_UPDATE, (anim, frame) => {
      if(frame.index == 3){
        this.physics.add.overlap( this.spikes, this.cPlayer, () => {
          health--;
        });
      } 
    });
    }
JackF99
  • 125
  • 6

1 Answers1

1

Well there many ways to do this, my first idea is use the animation update event, and dealing damage on one (or more) specific frame indices.
In the event callback you only need to check if the player overlaps with the sprite and if the right frame is showing.

Assuming the spikes are going up and down only one frame would deal damage.

Here a short demo showcasing this (Updated to better solution):
In this updated Demo the Damage will occur only on frameindex == 3 and only damage every second. (this is done with the setTimeout function). But if you don't need to limit on with frame damage should happen, you can remove this part spikeSprite.anims.currentFrame.index == 3, from the if-clause.

document.body.style = 'margin:0;padding:5px';

class GameScene extends Phaser.Scene {
    constructor () {
        super();
    }
    preload () {
        /** CREATE SPRITE FRAME FOR ONLY DEMO --- START */
        const canvasFrame = this.textures
            .createCanvas('spikes', 60, 10);

        let ctx = canvasFrame.context;

        ctx.fillStyle = '#00FF33';
        ctx.fillRect(0, 0, 10, 10);
        
        ctx.fillStyle = '#FAFF00';
        ctx.fillRect(10, 0, 10, 10);
        
        ctx.fillStyle = '#FF0000';
        ctx.fillRect(20, 0, 10, 10);
        
        canvasFrame.add(1, 0, 0, 0, 10, 10);
        canvasFrame.add(2, 0, 10, 0, 10, 10);
        canvasFrame.add(3, 0, 20, 0, 10, 10);
        
        canvasFrame.refresh();
        
        /** CREATE SPRITE FRAME FOR ONLY DEMO --- END*/

        this.anims.create({
            key: 'pulse',
            frames: this.anims.generateFrameNumbers('spikes', { start: 1, end: 3 }),
            frameRate: 1,
            repeat: -1
        });
    }

    create(){
         let damageCounter = 0; 
         let infoText = this.add.text(10, config.height - 10, 'No Damage')
            .setOrigin(0, 1)

         let spikes = this.add.sprite(30, 30, 'spikes')
            .play('pulse')
            .setScale(3)
            .setOrigin(0);
            
         let player = this.add.rectangle(10, 10, 30, 30, 0xffffff)
             .setOrigin(0);
             
         this.physics.add.existing(spikes)
         this.physics.add.existing(player)
         
         this.cursor = this.input.keyboard.createCursorKeys();
         let wasHitWithSpike = false;
         this.physics.add.overlap(spikes, player, (spikeSprite, player) => {
              if( wasHitWithSpike == false && spikeSprite.anims.currentFrame.index == 3){
                  damageCounter++;
                  wasHitWithSpike = true;
                  let text = ` -> taking damage\nOverlapping\nDamage: ${damageCounter}`;
                  infoText.setText(text);
                  // here you can tweak how long between damages
                  setTimeout( () => wasHitWithSpike = false, 1000);
              }
          });

         /* OLD Animation based part
           spikes.on(Phaser.Animations.Events.ANIMATION_UPDATE, (anim, frame, ...rest) => {
              let text = '';
              if(frame.index == 3){
                  // Add damage
                  infoText.setText(`frameindex:${frame.index}\nNot Overlapping\nDamage: ${damageCounter}`);
                  this.physics.overlap( spikes, player, () => {
                      damageCounter++;
                      text = `frameindex:${frame.index} -> taking damage\nOverlapping\nDamage: ${damageCounter}`;
                      infoText.setText(text);
                  });
              } else {
                  infoText.setText(`frameindex:${frame.index}\n\nDamage: ${damageCounter}`);
              }
         });*/
         
         this.player = player;
    }
    
    update(){
        let speed = 100;
        
        if(!this.cursor || !this.player){
            return
        }
        
        this.player.body.setVelocity(0);
        
        if(this.cursor.down.isDown){
            this.player.body.setVelocityY(speed);
        } else if(this.cursor.up.isDown){
            this.player.body.setVelocityY(-speed);
        } else if(this.cursor.left.isDown){
            this.player.body.setVelocityX(-speed);
        } else if(this.cursor.right.isDown){
            this.player.body.setVelocityX(speed);
        }
    }
}

var config = {
    type: Phaser.AUTO,
    width: 536,
    height: 140,
    physics: { default: 'arcade' },
    scene: [ GameScene ],
    banner: false
}; 

new Phaser.Game(config);
<script src="//cdn.jsdelivr.net/npm/phaser/dist/phaser.min.js"></script>
<div style="font-family:Arial">
Use the <b>arrow keys</b> to move the player (white square)<br>
Damage will only be dealt on the <span style="color:red"> red frame</span><br>
</div>

Update specific for your code:

You have to add the eventlistener on each sprite, you can't add it on the Group.

map.forEachTile(tile => {
    //Generate sprites on specific map tiles
    if (tile.index == 13) {
        let spike = this.spikes.create(tile.pixelX + 8, tile.pixelY + 8, "spikes")
            .play('spikes')
            .setDepth(10);

        spike.on(Phaser.Animations.Events.ANIMATION_UPDATE, (anim, frame) => {
            if(frame.index == 3){
                this.physics.overlap( spike, this.cPlayer, () => {
                    health--;
                });
            } 
        });
    }
});

Update 2, maybe better solution:

this solution might have a better performance (because it uses only one event listener), and needs fewer lines of code.

this.physics.add.overlap( this.cPlayer, this.spikes, (player, spikeSprite) => {
    if(spikeSprite.anims.currentFrame.index == 3){
        health--;
    }
});

Here the trigger is the overlap event (documentation), and than you only have to check the animation frame index of the sprite that overlaps with the player. And you want need the Phaser.Animations.Events.ANIMATION_UPDATE.

btw.: becareful since:
this.physics.overlap(...) checks once for an overlap (when it is called),
this.physics.add.overlap(...) adds an eventlister, that triggers everytime an overlap occurs

winner_joiner
  • 12,173
  • 4
  • 36
  • 61
  • I tried your code a bunch n made some edits to change it to my code and it just wont work at all. The damage function you created does not run which I tested by trying to create images at the start of the function. The example was very helpful to create the code, but im not sure where my mistake is right now, let me know if u can figure it out. (Updated the question, you can check the changes there) – JackF99 May 10 '23 at 02:02
  • 1
    @JackF99 I update my answer, this such solve the issue. – winner_joiner May 10 '23 at 04:50
  • 1
    @JackF99 I update my answer, I would recommend using the second update. – winner_joiner May 10 '23 at 05:43