EsotericSoftware / spine-runtimes

2D skeletal animation runtimes for Spine.
http://esotericsoftware.com/
Other
4.43k stars 2.92k forks source link

[spine-phaser] bounds not correct while using physics and scaleX -1 #2425

Open StreakingMan opened 1 year ago

StreakingMan commented 1 year ago

Here's my test of the mix-and-match-example.html, which has left me confused:

https://github.com/EsotericSoftware/spine-runtimes/assets/30397306/79b898ea-45c3-4adf-a2e7-4286b5f61f13

What I did was add physics and use scaleX=-1 to turn left.

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="//cdn.jsdelivr.net/npm/phaser@3.60.0/dist/phaser.js"></script>
    <title>Spine Phaser Example</title>
</head>

<body>
    <h1>Mix and match</h1>
</body>
<script>
    const config = {
        type: Phaser.AUTO,
        width: 800,
        height: 600,
        type: Phaser.WEBGL,
        scene: {
            preload: preload,
            create: create,
            update: update,
            pack: {
                files: [
                    { type: "scenePlugin", key: "spine.SpinePlugin", url: "../dist/iife/spine-phaser.js", sceneKey: "spine" }
                ]
            }
        },
        physics: {
            default: 'arcade',
            arcade: {
                debug: true,
                gravity: { y: 200 },
            },
        },
    };

    const game = new Phaser.Game(config);

    function preload() {
        this.load.spineBinary("mix-and-match-data", "assets/mix-and-match-pro.skel");
        this.load.spineAtlas("mix-and-match-atlas", "assets/mix-and-match-pma.atlas");
    }

    let cursors;
    let mixAndMatch;
    function create() {
        mixAndMatch = this.add.spine(400, 500, 'mix-and-match-data', "mix-and-match-atlas", new spine.SkinsAndAnimationBoundsProvider(null, ["full-skins/girl"]));
        mixAndMatch.scale = 0.5;
        mixAndMatch.animationState.setAnimation(0, "walk", true);

        this.physics.add.existing(mixAndMatch);
        mixAndMatch.body.setCollideWorldBounds(true);

        const skeletonData = mixAndMatch.skeleton.data;
        const skin = new spine.Skin("custom");
        skin.addSkin(skeletonData.findSkin("skin-base"));
        skin.addSkin(skeletonData.findSkin("nose/short"));
        skin.addSkin(skeletonData.findSkin("eyelids/girly"));
        skin.addSkin(skeletonData.findSkin("eyes/violet"));
        skin.addSkin(skeletonData.findSkin("hair/brown"));
        skin.addSkin(skeletonData.findSkin("clothes/hoodie-orange"));
        skin.addSkin(skeletonData.findSkin("legs/pants-jeans"));
        skin.addSkin(skeletonData.findSkin("accessories/bag"));
        skin.addSkin(skeletonData.findSkin("accessories/hat-red-yellow"));
        mixAndMatch.skeleton.setSkin(skin);
        mixAndMatch.skeleton.setToSetupPose();

        if (this.input.keyboard) cursors = this.input.keyboard.createCursorKeys();
    }

    function update(){
        if (cursors.left.isDown) {
            mixAndMatch.scaleX = -0.5;
        }else {
            mixAndMatch.scaleX = 0.5;
        }
    }
</script>

</html>

Is there something wrong with the Spine source file, or am I using scaleX incorrectly?

badlogic commented 1 year ago

You're not using it incorrectly. This appears to be a bug triggered by adding a physics component. I'm not super familiar with that Phaser sub-system, so I'm not sure what's going wrong. I'll update the issue once I've figured it out.

davidetan commented 7 months ago

In Phaser, if you use arcade physics and set the scaleX to a negative number will result in having the physics body reflected regardless on the usage of a SpineGameObject or a normal GameObject.

It's possible to get the very same result of the video on this phaser playground example adding block.scaleX = -block.scaleX; as latest line of the create function. The debug bounding box is reflected on the right of the brown box:

image

We have to consider that to flip a GameObject in Phaser, it seems that there are flip methods. In this case the setFlipX(boolean) should be used. It's important to notice this part of the Phaser flip documentation:

If this Game Object has a physics body, it will not change the body. This is a rendering toggle only.

So in general it's the user that has to take care of the GameObject body position for some operation (such as rotation) on the GameObject - at least for arcade physics.

However, when using setFlipX(boolean) on a SpineGameObject, it is not flipped. We should do that probably and will make a fix to it.

In any case, my advice is to explore also the matter physics system. That is definitely more advanced and your use case would work out of the box. Indeed, scaling with negative numbers in matter won't produce the effect of reflecting the GameObject body.

StreakingMan commented 5 months ago

@davidetan Thank you for your reply, I have tried the matter physics system, scaling with negative numbers works correctly, but the bounds position is still not right since the initiation.

https://github.com/EsotericSoftware/spine-runtimes/assets/30397306/df18f7e9-7804-4135-a3fc-af35234e436f

I have tried using setOrigin and providing a custom bounds provider, but it doesn't work. I'm struggling where the problem is, Here's the code:

mix-and-match-example.html ``` html Spine Phaser Example

Mix and match

```