Как я могу проверить перекрытие во время одного кадра спрайта? И вычесть только 1 из здоровья игроков за каждый цикл, когда игрок перекрывает кадр?

У меня есть спрайт с шипами, в цикле которого 4 кадра. Я хочу вычесть 1 из переменной здоровья, когда игрок перекрывается с 3-м кадром спрайта шипа.

В настоящее время функция .on не загружается должным образом. Моя игра работает, но функция перекрытия вообще не работает. Я отредактировал полученный код и удалил аспекты, которые, по моему мнению, не нужны (протестировал исходный образец кода, чтобы проверить, будет ли он работать, и мои изменения все еще работали. Также не работали с полным кодом, который мне прислали)

Мой рабочий код ниже,

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;
                    },
                });
       }
     });

Мой полный код (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--;
        });
      } 
    });
    }
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
3
0
56
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ну, есть много способов сделать это, моя первая идея — использовать событие обновления анимации и наносить урон одному (или нескольким) конкретным индексам кадра.
В обратном вызове события вам нужно только проверить, перекрывается ли игрок со спрайтом и отображается ли правый кадр.

Если предположить, что шипы движутся вверх и вниз, только один кадр нанесет урон.

Вот короткая демонстрация, демонстрирующая это (обновлено до лучшего решения):
В этой обновленной демо-версии урон будет наноситься только frameindex == 3 и только каждую секунду. (это делается с помощью функции setTimeout). Но если вам не нужно ограничение на то, что должно произойти повреждение рамы, вы можете удалить эту часть spikeSprite.anims.currentFrame.index == 3 из пункта if.

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>

Обновление для вашего кода:

Вы должны добавить прослушиватель событий в каждый спрайт, вы не можете добавить его в группу.

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--;
                });
            } 
        });
    }
});

Обновление 2, возможно, лучшее решение:

это решение может иметь лучшую производительность (поскольку оно использует только один прослушиватель событий) и требует меньше строк кода.

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

Здесь триггером является событие overlap (документация), а затем вам нужно только проверить индекс кадра анимации спрайта, который перекрывается с игроком. И вы хотите нужно Phaser.Animations.Events.ANIMATION_UPDATE.

Кстати: будьте осторожны, так как:
this.physics.overlap(...) проверяет один раз на перекрытие (при вызове),
this.physics.add.overlap(...) добавляет список событий, который срабатывает каждый раз, когда происходит перекрытие

Я пробовал ваш код несколько раз и внес некоторые изменения, чтобы изменить его на мой код, и он просто не работает. Созданная вами функция повреждения не запускается, что я проверил, пытаясь создать изображения в начале функции. Пример был очень полезен для создания кода, но я не уверен, где моя ошибка прямо сейчас, дайте мне знать, если вы можете понять это. (Обновил вопрос, вы можете проверить изменения там)

JackF99 10.05.2023 04:02

@ JackF99 Я обновляю свой ответ, это решает проблему.

winner_joiner 10.05.2023 06:50

@JackF99 JackF99 Я обновляю свой ответ, я бы рекомендовал использовать второе обновление.

winner_joiner 10.05.2023 07:43

Другие вопросы по теме