spine-runtimes
spine-runtimes copied to clipboard
[spine-phaser] bounds not correct while using physics and scaleX -1
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/[email protected]/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?
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.
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:
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.
@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
<!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/[email protected]/dist/phaser.js"></script>
<link rel="stylesheet" href="../../index.css" />
<title>Spine Phaser Example</title>
</head>
<body class="p-4 flex flex-col items-center">
<h1>Mix and match</h1>
</body>
<script>
const config = {
type: Phaser.AUTO,
width: 1000,
height: 1000,
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: 'matter',
matter: {
debug: true,
gravity: { y: 20 },
},
},
},
};
const game = new Phaser.Game(config);
let cursors;
let mixAndMatch;
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"
);
}
function create() {
mixAndMatch = this.add.spine(
400,
500,
"mix-and-match-data",
"mix-and-match-atlas",
new spine.SkinsAndAnimationBoundsProvider(null, ["full-skins/girl"])
// {
// calculateBounds(gameObject) {
// return {
// x: 0,
// y: -100, // nothing happen when change y
// width: 100,
// height: 100,
// }
// }
// }
);
// mixAndMatch.scale = 0.5;
mixAndMatch.animationState.setAnimation(0, "walk", 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();
// mixAndMatch.setOrigin(0.5, 0); // not work
this.matter.world.setBounds(0, 0, 1000, 1000);
this.matter.add.gameObject(mixAndMatch);
cursors = this.input.keyboard.createCursorKeys();
}
function update(){
if (cursors.left.isDown) {
mixAndMatch.scaleX = -1;
}else {
mixAndMatch.scaleX = 1;
}
}
</script>
</html>