• Runtimes
  • Skeleton Rotation Issues

If I have an animation looping and rotate my player it works just fine. The problem occurs when I want to play an animation and not loop afterwards. The skeleton does not rotate. I have narrowed it down to a Spine issue because the polygon is still rotating regardless of the animation. I have tried multiple animations and they all produce the same result. I am using a PolygonSpriteBatch to render my player. Any insight on the subject would be greatly appreciated as I am stumped 🙁

Related Discussions
...

Your description isn't much to go on. If you can make an SSCCE that would help. Eg, modify SimpleTest1 to show the problem.

Should I just paste the code here? How will you get the spine asset for the player? Also thank you very much for responding so quickly 🙂


I am just going to paste the code, I also tried it with a different spine animation and it produced the same results.

public class SimpleTest1 extends ApplicationAdapter {
   OrthographicCamera camera;
   PolygonSpriteBatch batch;
   SkeletonMeshRenderer renderer;
   SkeletonRendererDebug debugRenderer;

   TextureAtlas atlas;
   Skeleton skeleton;
   AnimationState state;
   
float rotation; float accelerationY = -460; float vx, vy, x = 50, y = 300; float width; float height;
public void create () { camera = new OrthographicCamera(); batch = new PolygonSpriteBatch(); renderer = new SkeletonMeshRenderer(); renderer.setPremultipliedAlpha(true); // PMA results in correct blending without outlines. debugRenderer = new SkeletonRendererDebug(); debugRenderer.setBoundingBoxes(false); debugRenderer.setRegionAttachments(false); atlas = new TextureAtlas(Gdx.files.internal("spine/player/skeleton.atlas")); SkeletonJson json = new SkeletonJson(atlas); // This loads skeleton JSON data, which is stateless. json.setScale(0.3f); // Load the skeleton at 30% the size it was in Spine. SkeletonData skeletonData = json.readSkeletonData(Gdx.files.internal("spine/player/skeleton.json")); skeleton = new Skeleton(skeletonData); // Skeleton holds skeleton state (bone positions, slot attachments, etc). AnimationStateData stateData = new AnimationStateData(skeletonData); // Defines mixing (crossfading) between animations. state = new AnimationState(stateData); // Holds the animation state for a skeleton (current animation, time, etc). state.setTimeScale(5f); /* When set this high the player finishes the animation and because of this he does not rotate. When an animation is playing, such if I were set looping to true there is no issue with the player rotating. Set the TimeScale to .5f and you will see he rotates just fine or set looping to true. */ state.setAnimation(0, "Moving", false); /*I didn't find a method to get the scaled height or width and this seemed to work perfectly, I bet some users would like to see this be added :)*/ width = skeleton.getData().getWidth() * .3f; // Multiply by the same scale set.... (.3) height = skeleton.getData().getHeight() * .3f; // Multiply by the same scale set.... (.3) } public void render () {
if (Gdx.input.isKeyJustPressed(Keys.SPACE)) vy = 300; state.update(Gdx.graphics.getDeltaTime()); // Update the animation time. updatePlayer(Gdx.graphics.getDeltaTime()); // Update the player. Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); state.apply(skeleton); // Poses skeleton using current animations. This sets the bones' local SRT. skeleton.updateWorldTransform(); // Uses the bones' local SRT to compute their world SRT. // Configure the camera, PolygonSpriteBatch, and SkeletonRendererDebug. camera.update(); batch.getProjectionMatrix().set(camera.combined); debugRenderer.getShapeRenderer().setProjectionMatrix(camera.combined); batch.begin(); renderer.draw(batch, skeleton); // Draw the skeleton images. batch.end(); debugRenderer.draw(skeleton); // Draw debug lines. }
public void updatePlayer(float delta) { vy += accelerationY * delta;
y += vy * delta; skeleton.setPosition(x, y); // Rotate counterclockwise // Adding to rotation moves left if (vy > 0) { rotation += 600 * delta; if (rotation > 0) { rotation = 0; } } // Rotate clockwise // Subtracting from rotation moves right if (vy < -50) { rotation -= 320 * delta; if (rotation < -90) { rotation = -90; } } skeleton.getRootBone().getData().setRotation(rotation); } public void resize (int width, int height) { camera.setToOrtho(false); // Update camera with new size. } public void dispose () { atlas.dispose(); } public static void main (String[] args) throws Exception { new LwjglApplication(new SimpleTest1()); } }

You can use spineboy so I can run the code.

Your code changes the local rotation of the root bone, then you apply an animation. If that animation keys the root bone, it will overwrite your rotation change. Try moving updatePlayer after state.apply but before skeleton.updateWorldTransform.

I didn't find a method to get the scaled height or width and this seemed to work perfectly,
I bet some users would like to see this be added 🙂

The scale you mention is the scale of the atlas, not the skeleton data. It isn't stored in the atlas, but since you are loading the atlas you should know what scale it uses.

I took your advice and moved updatePlayer after state.apply, and before skeleton.updateWorldTransform and the problem persists 🙁

I am confused on the scale of the atlas, and what you mean exactly. I do understand that yes you should know the scale since you set the scale when loading the assets that's why I used this and was able to make a polygon with the correct size after being scaled. Wouldn't making a method that returns the original height and width multiplied by its' scale factor if it's been set to other than 1 be helpful, or is there another way of doing it? I don't mean to be pushy please don't take it that way I am just curious and am trying to fully understand the code that is happening.

Thanks again for your patience and help, I hope we can figure out this rotation issue.

His code actually changes setup pose rotation. So it should work under the effects of animations since animations only change the instanced root bone transform, which is applied on top of setup pose.

According to your code, the rotation is clamped between 0 and -90?
But positive rotation advances whenever vy is positive.
Negative rotation advances whenever vy is less than -50.
Are these conditions fulfilled properly in your program?
Have you tried doing the rotations without these conditions?

Yes all conditions are being met, the reason for the -50 is because I want it to look like the player is falling for a second then rotate him toward the ground like he is free falling. I currently have acceleration set to -460 which is definitely making vy less than -50, pretty quickly as well. I tried changing the -50 to 0 and still the same results. The clamp is to keep the player upright or rotate back to an upright position when gaining height and do a superman pose lol. When the player starts to fall, rotate toward the ground and freefall. I want to play those animations without looping and just rotate myself, but if the animation is not looping it doesn't rotate.

I think I get it. The setup pose values are only used at all if you .apply an animation to the skeleton. Otherwise, changing it doesn't affect instances using that skeleton data.

I think you need to change this line:

skeleton.getRootBone().getData().setRotation(rotation); // changes setup pose rotation in skeleton data

to this

skeleton.getRootBone().setRotation(rotation); // changes skeleton bone instance rotation

And, as Nate mentioned, you need to make sure this call happens after state.apply(skeleton) but before skeleton.updateWorldTransform().
OR you need to make sure your animation doesn't key root bone rotation.

Thanks Pharan. it finally worked! I was seriously beating my head against the wall trying to figure this out. I almost wish I would have came here sooner, but I don't like to flood the forums or ask for help if I can do it myself. Thanks again man, and while we are on the subject (kind of) I noticed something else. If I play an animation and do not give it any velocity like just have it sit still I noticed that the SkeletonBounds that I use does not work, only if the object is actually moving.... velocity > 0 Any clue as to why? One more question about the SkeletonBounds, do you have to have a separate instance for each object that you want to use the bounds on, how do I get the bounds for different animations if not? Once again I really appreciate both of your guys' help 🙂 I currently only use the bounds for one character and make my own polygons for all of the others. I am just curious on how to use the bounds for more than one spine animation.

Oops, I didn't notice you were changing setup pose values either, so don't feel bad you missed it. All hail Pharan! But next time post executable code and you will get better and faster assistance.

I'm not sure about your velocity problem from that description. Sounds pretty application specific.

SimpleTest2 in spine-libgdx shoulds SkeletonBounds. You can use the same SkeletonBounds instance for many skeletons (read the source, it is pretty simple). You give it a Skeleton and the SkeletonBounds collects all the BoundingBoxAttachments and their polygon vertices. You can use those directly, or you can use the SkeletonBounds convenience methods. You'd need multiple SkeletonBounds if you wanted BoundingBoxAttachments and polygon vertices for two different Skeletons at the same time.

Thanks a lot Nate! I'll go give it a whirl.