alex
alex

Reputation: 611

Phaser 3: Change "Hitbox"/Interactive area of sprite without physics

The game I'm creating doesn't require any physics, however you are able to interact when hovering over/clicking on the sprite by using sprite.setInteractive({cursor: "pointer"});, sprite.on('pointermove', function(activePointer) {...}); and similar. However I noticed two issues with that:

  1. The sprite has some area which are transparent. The interactive functions will still trigger when clicking on those transparent areas, which is unideal.

  2. When playing a sprite animation, the interactive area doesn't seem to entirely (at all?) change, thus if the sprite ends on a frame bigger than the previous, there end up being small areas I can't interact with.

One option I thought of was to create a polygon over my sprite, which covers the area I want to be interactive. However before I do that, I simply wanted to ask if there are simpler ways to fix these issues.

Upvotes: 5

Views: 3078

Answers (2)

winner_joiner
winner_joiner

Reputation: 14915

This might not be the best solution, but I would solve this problem like this. (If I don't want to use physics, and if it doesn't impact the performance too much)

I would check in the event-handler, if at the mouse-position the pixel is transparent or so, this is more exact and less work, than using bounding-boxes.

You would have to do some minor calculations, but it should work well.

btw.: if the origin is not 0, you would would have to compensate in the calculations for this. (in this example, the origin offset is implemented)

Here is a demo, for the click event:

let Scene = {
    preload ()
    {
        this.load.spritesheet('brawler', 'https://labs.phaser.io/assets/animations/brawler48x48.png', { frameWidth: 48, frameHeight: 48 });
    },
    create ()
    {
        // Animation set
        this.anims.create({
            key: 'walk',
            frames: this.anims.generateFrameNumbers('brawler', { frames: [ 0, 1, 2, 3 ] }),
            frameRate: 8,
            repeat: -1
        });
        
        // create sprite
        const cody = this.add.sprite(200, 100).setOrigin(0);
        cody.play('walk');
        cody.setInteractive();
        
        // just info text
        this.mytext =  this.add.text(10, 10, 'Click the Sprite, or close to it ...', { fontFamily: 'Arial' });

        // event to watch
        cody.on('pointerdown', function (pointer) {
            // calculate x,y position of the sprite to check
            let x = (pointer.x - cody.x) / (cody.displayWidth / cody.width)
            let y = (pointer.y - cody.y) / (cody.displayHeight / cody.height);
            
            // just checking if the properties are set
            if(cody.anims && cody.anims.currentFrame){
                let currentFrame = cody.anims.currentFrame;
                let pixelColor = this.textures.getPixel(x, y, currentFrame.textureKey, currentFrame.textureFrame);

                // alpha > 0 a visible pixel of the sprite, is clicked 
                if(pixelColor.a > 0) {
                    this.mytext.text = 'Hit';
                } else {
                    this.mytext.text = 'No Hit';
                }
                
                // just reset the textmessage
                setTimeout(_ => this.mytext.text = 'Click the Sprite, or close to it ...' , 1000);
            }
        }, this);
    }
};

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

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

Upvotes: 3

djdabs_
djdabs_

Reputation: 76

Was trying to find an answer for this myself just now..

Think Make Pixel Perfect is what you're looking for.

this.add.sprite(x, y, key).setInteractive(this.input.makePixelPerfect());

https://newdocs.phaser.io/docs/3.54.0/focus/Phaser.Input.InputPlugin-makePixelPerfect

Upvotes: 6

Related Questions