Phaser 3:检查可拖动对象是否在 Sprite 的交互区域上方

Phaser 3: Check if draggable Object is over interactive area of a Sprite

我想实现的是能够拖动一个Sprite,当它移动到另一个Sprite的交互区域时,它会发射粒子。

但是,如果我正在拖动另一个精灵,则不会调用 Pointermove 事件(可能是因为我的指针位于可拖动精灵上方,而不是交互式精灵上方。)我尝试向我的拖动事件添加 If-Condition ,它测试我的可拖动对象是否位于交互式 Sprite 上方,这种方法可行,但它会在 sprite 的任何区域上做出反应,而不是仅在交互式区域上。这很重要,因为我的 Sprite 的交互区域使用 pixel perfect.

有什么方法可以达到我想要的结果吗?

我对 Pointermove 的尝试:

gameState.chara.setInteractive({cursor: "pointer", pixelPerfect: true});

gameState.chara.on('pointermove', function(activePointer){ 
    if (activePointer.isDown && gameState.clean_cursor) {
        switch(gameState.clean_cursor.frame.name.split('_')[0]){
        case "sponge":
            gameState.bubble.emitParticle(1, activePointer.x, activePointer.y);
            break;
        case "showerhead":
            gameState.splash.emitParticle(1, activePointer.x-50, activePointer.y+50);
            break;
        }
    }
});

this.input.on('drag', (activePointer, gameObject, dragX, dragY) => {
    switch(gameObject.frame.name.split('_')[0]){
    case "sponge":
        gameState.clean_cursor.play('sponge_cursor', true);
        break;
    case "showerhead":
        gameState.clean_cursor.play('shower_cursor', true);
        break;
    }
        
    gameObject.x = dragX;
    gameObject.y = dragY;
});
    
this.input.on('dragend', function(pointer, gameObject) {
    gameObject.destroy();
    gameState.clean_cursor = false;
});

(一旦我拖动另一个精灵,Pointermove 事件就会停止反应)

我只尝试拖动:

gameState.chara.setInteractive({cursor: "pointer", pixelPerfect: true});

this.input.on('drag', (activePointer, gameObject, dragX, dragY) => {
    if(gameState.chara.getBounds().contains(dragX, dragY)) {
        switch(gameState.clean_cursor.frame.name.split('_')[0]){
        case "sponge":
            gameState.bubble.emitParticle(1, activePointer.x, activePointer.y);
            break;
        case "showerhead":
            gameState.splash.emitParticle(1, activePointer.x-50, activePointer.y+50);
            break;
        }
    }

    switch(gameObject.frame.name.split('_')[0]){
    case "sponge":
        gameState.clean_cursor.play('sponge_cursor', true);
        break;
    case "showerhead":
        gameState.clean_cursor.play('shower_cursor', true);
        break;
    }
        
    gameObject.x = dragX;
    gameObject.y = dragY;
});
    
this.input.on('dragend', function(pointer, gameObject) {
    gameObject.destroy();
});

(有效,但在整个精灵上,而不是仅 Pixel Perfect 交互区域)

有了解决方法如何拖动 button/image

这个题的解法应该很简单

  • 只需检查精灵是否与边界重叠。 (不是像素完美的,因为它匹配框)here the documentation

如果需要pixel-perfect,这个就比较难了

这是“简单”解决方案的代码:
(代码需要一些调整)

let Example = {
    preload () {
        this.load.spritesheet('brawler', 'https://labs.phaser.io/assets/animations/brawler48x48.png', { frameWidth: 48, frameHeight: 48 });
            this.load.atlas('flares', 'https://labs.phaser.io/assets/particles/flares.png', 'https://labs.phaser.io/assets/particles/flares.json');
    },
    create () {
    
        this._particles = this.add.particles('flares');

        this._emitter = this._particles.createEmitter({
            frame: 'blue',
            lifespan: 2000,
            speed: { min: 50, max: 400 },
            angle: 330,
            gravityY: 300,
            scale: { start: 0.4, end: 0 },
            quantity: 2,
            blendMode: 'ADD',
            on: false
        });
        
        let buttonPos = { x:20, y:20};
        let buttonPlaceholder = this.add.image(buttonPos.x, buttonPos.y, 'brawler', ).setOrigin(0.5);
        let button = this.add.image(buttonPos.x, buttonPos.y, 'brawler', ).setOrigin(0.5);
        
        let interact = this.add.image(200, 100, 'brawler', 3).setOrigin(0.5);
        
        interact.setDepth(-1); 

        button.setInteractive({ useHandCursor: true });
        this.input.setDraggable(button);
        
        this.input.on('drag', function(pointer, gameObject, dragX, dragY) {
            gameObject.x = dragX;
            gameObject.y = dragY;
            if(Phaser.Geom.Intersects.RectangleToRectangle(gameObject.getBounds(), interact.getBounds() )){
                this._emitter.start()
                this._particles.x = interact.x;
                this._particles.y = interact.y;
            } else {
              if (this._emitter.active){
                   this._emitter.stop();
              }
            }
        }, this);
        
        this.input.on('dragend', function(pointer, gameObject) {
            gameObject.x = buttonPos.x;
            gameObject.y = buttonPos.y;
          this._emitter.stop();
        }, this);
    }
}

const config = {
    type: Phaser.AUTO,
    width: 400,
    height: 200,
    scene: [ Example ]
};

const game = new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser@3.55.2/dist/phaser.js"></script>

Extra info, to improve: You could check overlap with CircleToCircle, and keeping the radius abit smaller. (Or other intersection functions, that fit your needs better https://photonstorm.github.io/phaser3-docs/Phaser.Geom.Intersects.html)

像素完美版,可以这样写:
(刚找到这个属性this.input.topOnly,docs)
如果设置为 false,此 属性 允许将输入事件传递给位于最顶层对象下的其他对象。

let Example = {
    preload () {
        this.load.spritesheet('brawler', 'https://labs.phaser.io/assets/animations/brawler48x48.png', { frameWidth: 48, frameHeight: 48 });
            this.load.atlas('flares', 'https://labs.phaser.io/assets/particles/flares.png', 'https://labs.phaser.io/assets/particles/flares.json');
    },
    create () {
    
        this.input.topOnly = false;

        let isDragging = false;
        
        this._particles = this.add.particles('flares');

        this._emitter = this._particles.createEmitter({
            frame: 'blue',
            lifespan: 2000,
            speed: { min: 400, max: 600 },
            angle: 330,
            gravityY: 300,
            scale: { start: 0.4, end: 0 },
            quantity: 2,
            blendMode: 'ADD',
            on: false
        });
        
        let buttonPos = { x:20, y:20};
        let buttonPlaceholder = this.add.image(buttonPos.x, buttonPos.y, 'brawler', )
             .setOrigin(0.5);
             
        let button = this.add.image(buttonPos.x, buttonPos.y, 'brawler', )
             .setOrigin(0.5);
        
        let interact = this.add.image(200, 100, 'brawler', 3)
             .setOrigin(0.5);

        interact
         .setInteractive(this.input.makePixelPerfect());

        interact.on('pointerover', function(pointer){
            if(isDragging){
                this._particles.x = interact.x;
                this._particles.y = interact.y;
                this._emitter.start();
            }
        }, this);
        
        interact.on('pointerout', function(pointer){
            this._emitter.stop();
        }, this);
        
        interact.setDepth(-1); 

        button.setInteractive({ useHandCursor: true });

        this.input.setDraggable(button);
        
        this.input.on('drag', function(pointer, gameObject, dragX, dragY) {
            isDragging = true;
            gameObject.x = dragX;
            gameObject.y = dragY;

        }, this);
        
        this.input.on('dragend', function(pointer, gameObject) {
            gameObject.x = buttonPos.x;
            gameObject.y = buttonPos.y;
            this._emitter.stop();
            isDragging = false;
        }, this);
    }
}

const config = {
    type: Phaser.AUTO,
    width: 400,
    height: 200,
    scene: [ Example ]
};

const game = new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser@3.55.2/dist/phaser.js"></script>