azerion / phaser-spine

A plugin for Phaser 2 that adds Spine support
MIT License
121 stars 58 forks source link

[Spine-TS] Group Container and Root Bone Position Problem #72

Open shibekin69 opened 7 years ago

shibekin69 commented 7 years ago

Hey guys,

Some things I noticed about the new Spine-TS:

When you put the Spine object inside a group, scaling a group does not affect the Spine object properly. In the old phaser-spine, you can scale the container group using Phaser, and the Spine object will scale accordingly. It doesn't happen here. It seems you need to scale the Spine object itself.

How the object is placed onto the stage is rather wonky and unpredictable. It doesn't seem to honor the Root bone's x and y coordinates nor Spine's 0,0 world coordinates. It seems to be somewhere on the top and left, maybe treating the Spine object as an image and laying it down like an image, where the 0,0 is the top left hand corner.

shibekin69 commented 7 years ago

Ok, I tried placing different sized animations in a group, and the 0,0 point is dependent on the size and is somewhere the top left of the entire animation body (is it treating it like an image?) . The centerX and centerY properties change. If the 0,0 position in spine equals the 0,0 world position of the Spine editor like in the old version, that would make things more predictable, but since it's always somewhere on the top left, this makes it unpredictable to work with.

shibekin69 commented 7 years ago

Based on my experiments, it seems that the origin/anchor position of the Spine animation is dependent on the width/height of it. So take the following for example, I have 2 animations, 1 that's full size, and the other shrunk to 20% (by importing a new Spine Data, altering the scale, and using image assets that's the same as the scale). I grabbed the Spine object's width and height, and positioned the root bone until it very closely matches the parent group's x and y points (where scaling the spine object will be very close to using the root bone's position as the anchor of the scaling.)

100% Size: Spine Width (In Px, Phaser): 1830 Spine Height (In Px, Phaser): 1804 Approx. center where the root has to be in for scaling to work, using the root as the center: x: -700 y: 1242

20% Size: Spine Width (In Px, Phaser): 366 Spine Height (In Px, Phaser): 361 Approx. center where the root has to be in for scaling to work, using the root as the center: x: -140 y: 248

I don't know why it's like this, but I'm trying to find some meaning into these numbers. Maybe someone can figure this out one?

shibekin69 commented 7 years ago

Ok, when I set the anchor of the Spine object to (0.5, 0.5), it helps. I need to move around the skeleton's x and y (not bone[0]'s x and y) for me to be able to adjust the spine object's position so it can scale properly. If I cancel out root's X and Y offset by moving it to the opposite direction, it should move it down to the 0,0 position in Spine and in Phaser.

I have something like this my code to try and center the root bone to 0,0:

spineGroup.spineObj.anchor.setTo(0.5, 0.5);
spineGroup.spineObj.x = 0;
spineGroup.spineObj.y = 0;
spineGroup.spineObj.centerX = 0;
spineGroup.spineObj.centerY = 0;

spineGroup.spineObj.skeleton.x = -1 * spineGroup.spineObj.skeleton.bones[0].x;
spineGroup.spineObj.skeleton.y = 1 * spineGroup.spineObj.skeleton.bones[0].y;

I usually have my root on the upper right quadrant in the editor. If I do this, it seems to move X properly, but the Y, it can't figure out. It's always above the root! In other words, the root is always below it, when it's supposed to be centered already.

See pic below:

image

EDIT 1: Regardless of where I move the spine object with the root bone in Spine Editor, I end up with the above, which is good in a way, but the Y is just not right...

shibekin69 commented 7 years ago

Another observation between WebGL and Canvas. In WebGL, to get the same result, the Y needs to be negative. In the above example, if Y was set to 2000px, this needs to be -2000px in WebGL.

EDIT: If you move the spine object's root to 0,0 in the Spine Editor's space, WebGL positioning will be the same as Canvas.

shibekin69 commented 7 years ago

@AleBles - Do you see this problem on your end? This thing is making Spine-TS unusable and I can't find a fix for it :(

AleBles commented 7 years ago

I'm actually working on it inbetween :p

shibekin69 commented 7 years ago

Yeah, I'm just seeing whether it's just me or it's really a problem others can see too.

AleBles commented 7 years ago

Just to make sure we implement it correct: should the position you put down in Phaser always be the spot where the rootbone is located?

Then as a second question, how should we handle anchor's then?

In order for Phaser anchor's to work correctly you'd expect this lib to ignore the rootbone and just use the Phaser point as the top-left corner of the image.

Toughts @shibekin69 ?

Edit 1: This was not an issue in the old spine because the spine extended a Phaser.Group, therefor having no anchor, spine-ts extends Phaser.Sprite (which is better imho) but I feel like we should then also adhere to anchor (and therefor ignoring rootbone positions)

shibekin69 commented 7 years ago

Yes on the first question. I think that'd be most practical.

Hmm.. Is there a way to make it work both ways so that for 1.) those who want to use the root bone as the center of the spine object, and 2.) those who want to just use the top left of the image can use the plugin according to their preference?

Personally, I'd prefer to use the root bone as the center of the spine object with anchor 0.5, 0.5 so that it's predictable when I spawn it on screen and when I do transforms on it. Here's an idea, how about being able to set the anchor of the root when spawning a new spine object with 0.5, 0.5 as the default?

Something like this:

game.load.spine('spineboy', '/path/to/spineboy.json', {
  x: 100, // x position of root on the screen
  y: 100,  // y position of root on the screen
  anchorx: 0.5, //anchor x of root
  anchory: 0.5 //anchor y of root
})

Then just be able to change/tween the x, y, anchorx, and anchory any time after initialization.

The ability to change the anchor x and y of the root will let you change the way you scale the spine object. So for some games, like side scrollers (ex: contra), this might be set to where the feet's touching the ground (0.5, 1) and scaling it will make it grow upward and not clip the floor. Others who make maybe top down shooters, flying monsters, or other kinds of stuff, use 0.5, 0.5 or some other arbitrary number (like myself) to have an even scaling or some kind of scaling specific to their needs.

It's easier to use the root bone as the spawn point in phaser since it's very easy to also set it in the spine editor visually no? Doing invisible guess work with phaser was quite a pain.

AleBles commented 7 years ago

We could go for a simple boolean or such where the bound position can be changed on the fly, maybe even generic plugin setting?

shibekin69 commented 7 years ago

Bound position?

So in the mode where the root position is not going to be treated as the center of the spine object, the position set in phaser will be the 0,0 origin shown in the spine editor? I think this is how the current phaser-spine worked no?

AleBles commented 7 years ago

That's what I meant but after discussing with our artist am leaning more towards a solution where anchor and rootbone are the same thing.

So if you create a spine with your root bone in the death center if the animation, this will also be reflected towards the anchor (will be set to 0,5; 0,5). The moment you adjust the anchor we'll ignore the position of the rootbone and just anchor it according to regular sprite behaviour.

This means that the rootbone only dictates the default position of the anchor point and you're freely to adjust it anyway you like.

Example; a spine animation that is 200 wide and 400 heigh, with a rootbone positioned slightly above the bottom-middle will load up with an initial anchor of (0.5; 0.9) (ofcourse exactly calculated based on the spoine dimensions).

shibekin69 commented 7 years ago

Ah, that makes sense. Can you have a function that resets the anchor to the root bone's position?

On Aug 29, 2017 8:58 PM, "Ale Bles" notifications@github.com wrote:

That's what I meant but after discussing with our artist am leaning more towards a solution where anchor and rootbone are the same thing.

So if you create a spine with your root bone in the death center if the animation, this will also be reflected towards the anchor (will be set to 0,5; 0,5). The moment you adjust the anchor we'll ignore the position of the rootbone and just anchor it according to regular sprite behaviour.

This means that the rootbone only dictates the default position of the anchor point and you're freely to adjust it anyway you like.

Example; a spine animation that is 200 wide and 400 heigh, with a rootbone positioned slightly above the bottom-middle will load up with an initial anchor of (0.5; 0.9) (ofcourse exactly calculated based on the spoine dimensions).

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/orange-games/phaser-spine/issues/72#issuecomment-325655565, or mute the thread https://github.com/notifications/unsubscribe-auth/AL7YA9To3_BNrbADaEz9O8J6gjvThxGrks5sdAr_gaJpZM4O8WVo .

AleBles commented 7 years ago

So Canvas rendering should be good now. Moving on to WebGL ;x

shibekin69 commented 7 years ago

Ok, I'm testing out the canvas version. Moving the group container where it's in doesn't move the spine object. So if you do this:

spineContainerGroup.x = 200 <-- does not work (doesn't move spine object) spineContainerGroup.children[0] = 50 <-- works

I tried to make x, y, centerX, and centerY properties start with 0 and it as it is. The positioning problems still there. Hmm. I'm trying to make the root bone align with my spineContainerGroup's 0,0. And then what I do is apply transformations to the group and sometimes to the spine object itself.

EDIT: I'm trying out the spine-ts-position build

EDIT 2: Ok, I think I'm figuring this out now. It kinda behaves like the old phaser-spine. I got rid of trying to set 0 to the spine object's [x, y, centerX, centerY], so I only have the code to cancel out the root bone's [x, y] to bring it to the origin:

skeleton.x = -1 skeleton.bones[0].x skeleton.y = 1 skeleton.bones[0].y

And I'm seeing the root bone does align at the very least to the top edge of the screen (0,0 point of something). Scaling the spine object even when its in a container is at least predictable (scales accordingly). However, adjusting the group container's [x,y] doesn't move the spine object.

shibekin69 commented 7 years ago

Btw, I deal with large spine animations, spine width and heights that's anywhere between 2000 x 2000 to 3000 x 3000. And they're on the upper right quadrant of the spine editor, and my root bone's about 1000+ x and y pixels from the editor's origin. So there might be some weird problems that only appear when offsets are exaggerated because of this?

shibekin69 commented 7 years ago

This is probably related to the group.x and group.y not working right now with the spine-ts-position WIP build, I'm using the Juicy screen shake plugin. It doesn't shake the Spine objects along with the other stuff on screen.

shibekin69 commented 7 years ago

I've been playing around with different spine objects, and there's a weird x offset if there's horizontal white space between x = 0 up to the edge of your spine's images. I've only tested this with the spine stuff on the upper right quadrant for now. This can be remedied by multiplying anchor.x by -1.

Please see pic:

image

shibekin69 commented 7 years ago

Here are my observations for the other quadrants. Just an FYI. The most important I guess might be the upper right one, since that's where the PSD to Spine export script places the output.

It seems applying a negative / opposite anchor value only to the axis or axes that has this weird offset fixes the positioning.

Btw, I'm still using:

skeleton.x = -1 skeleton.bones[0].x skeleton.y = 1 skeleton.bones[0].y

To cancel out the root's (x,y) position. This assumes the object is in the upper right quadrant.

image

If it helps you someway, I have something like this in my code:

if (sprite.type === 'spine') {
  if (sprite.children.length > 0) {
    if (sprite.children[0].constructor.name === 'Spine') {
      //console.log('Sprite.x: ' + sprite.x);
      //console.log('Sprite.y: ' + sprite.y);
      sprite.children[0].x = sprite.x;
      sprite.children[0].y = sprite.y;

      //TEMP: Anchor Corrector
      if (sprite.children[0].skeleton.bones[0].x > 0 && sprite.children[0].skeleton.bones[0].y > 0) {
        // Upper right quadrant
        // X correction
        sprite.children[0].anchor.x *= -1;

      } else if (sprite.children[0].skeleton.bones[0].x < 0 && sprite.children[0].skeleton.bones[0].y > 0) {
        // Upper left quadrant

      } else if (sprite.children[0].skeleton.bones[0].x < 0 && sprite.children[0].skeleton.bones[0].y < 0) {
        // Bottom left quadrant
        // Y correction
        sprite.children[0].anchor.y *= -1;

      } else if (sprite.children[0].skeleton.bones[0].x > 0 && sprite.children[0].skeleton.bones[0].y < 0) {
        // Bottom right quadrant
        // X and Y correction
        sprite.children[0].anchor.x *= -1;
        sprite.children[0].anchor.y *= -1;
      }

    }
  } else if (sprite.constructor.name === 'Spine') {
    sprite.x = sprite.x;
    sprite.y = sprite.y;

    //TEMP: Anchor Corrector
    if (sprite.skeleton.bones[0].x > 0 && sprite.skeleton.bones[0].y > 0) {
      // Upper right quadrant
      // X correction
      sprite.anchor.x *= -1;

    } else if (sprite.skeleton.bones[0].x < 0 && sprite.skeleton.bones[0].y > 0) {
      // Upper left quadrant

    } else if (sprite.skeleton.bones[0].x < 0 && sprite.skeleton.bones[0].y < 0) {
      // Bottom left quadrant
      // Y correction
      sprite.anchor.y *= -1;

    } else if (sprite.skeleton.bones[0].x > 0 && sprite.skeleton.bones[0].y < 0) {
      // Bottom right quadrant
      // X and Y correction
      sprite.anchor.x *= -1;
      sprite.anchor.y *= -1;
    }
  }
}
AleBles commented 7 years ago

Pfff this is going in baby steps xD, Should be ok now for the Y axis, on to X ;x

shibekin69 commented 7 years ago

Proposed fix whilst keeping the adjustable anchor here:

https://github.com/orange-games/phaser-spine/commit/6241a742748a9ace3fa4446efe8f326a9b2dc0e4#commitcomment-24040207

ankush-badyal commented 6 years ago

Hi, Is this fixed? if yes then in which branch?

shibekin69 commented 6 years ago

No idea, I'm confused myself lol

likeavenus commented 1 year ago

I had a similar problem when I attached a hitbox to a brush bone and the Y value was mirrored. I think that spine can solve this problem, but I don't know how yet

// update method
const hitboxCoords = { x: leftArm.worldX, y: leftArm.worldY * -1 + this.game.canvas.height - 10 };
this.boy.physicsBody.position.copy(hitboxCoords);