У меня есть спрайт с шипами, в цикле которого 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--;
});
}
});
}



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Ну, есть много способов сделать это, моя первая идея — использовать событие обновления анимации и наносить урон одному (или нескольким) конкретным индексам кадра.
В обратном вызове события вам нужно только проверить, перекрывается ли игрок со спрайтом и отображается ли правый кадр.
Если предположить, что шипы движутся вверх и вниз, только один кадр нанесет урон.
Вот короткая демонстрация, демонстрирующая это (обновлено до лучшего решения):
В этой обновленной демо-версии урон будет наноситься только 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 Я обновляю свой ответ, это решает проблему.
@JackF99 JackF99 Я обновляю свой ответ, я бы рекомендовал использовать второе обновление.
Я пробовал ваш код несколько раз и внес некоторые изменения, чтобы изменить его на мой код, и он просто не работает. Созданная вами функция повреждения не запускается, что я проверил, пытаясь создать изображения в начале функции. Пример был очень полезен для создания кода, но я не уверен, где моя ошибка прямо сейчас, дайте мне знать, если вы можете понять это. (Обновил вопрос, вы можете проверить изменения там)