Rotate a Texture Animation in libGDX

Using an Animation instance is a common way to create an animated sprite in libGDX. To optimize rendering we apply Textures instead of Sprites. Rotating a Sprite is trivial (with setOrigin and rotate()). Now how can we rotate a Texture Animation?

Let’s take a look at the plane game example. The animation is created from a set of PNG assets:

Texture frame1 = new Texture("plane1.png");
frame1.setFilter(TextureFilter.Linear, TextureFilter.Linear);
Texture frame2 = new Texture("plane2.png");
Texture frame3 = new Texture("plane3.png");
 
plane = new Animation(0.05f, new TextureRegion(frame1), new TextureRegion(frame2), new TextureRegion(frame3), new TextureRegion(frame2));
plane.setPlayMode(PlayMode.LOOP);

It would be nice if we could adjust the plane’s pitch a little to its vertical movement. SpriteBatch offers some draw() methods with a signature to rotate the plane animation. They take a bunch of paramaters that fortunately can easily be calculated. The vertical velocity is available as the y component of the velocity vector (a Vector2 object). Here is the original animation rendering code in the drawWorld() method that is called from the game’s render() method:

batch.draw(plane.getKeyFrame(planeStateTime), planePosition.x, planePosition.y);

To add a pitch, declare the fields keyFrame and rotation, then replace the rendering code with this (note that the shape is at 270° when not rotated):

keyFrame = plane.getKeyFrame(planeStateTime);
rotation = 270;
if (planeVelocity.y > 30)
    rotation = 280;
if (planeVelocity.y < -30)
    rotation = 260;
batch.draw(keyFrame, planePosition.x, planePosition.y,
    keyFrame.getRegionWidth() / 2.0f,
    keyFrame.getRegionHeight() / 2.0f, keyFrame.getRegionWidth(),
    keyFrame.getRegionHeight(), 1f, 1f, rotation, false);

So now the plane will move in a slightly more natural looking way.

Plane Game Pitch

However, the plane appears to be somewhat stretched in the y direction and shrunken in the x direction. The eighth and the ninth parameter of the draw() method represent scaling in both directions. Since both are set to one we wouldn’t expect the observed behaviour (at least I wouldn’t). Though the rationale behind this remains a bit of a mystery to me, I solved it as follows:

batch.draw(keyFrame, planePosition.x, planePosition.y, keyFrame
    .getRegionWidth() / 2.0f, keyFrame.getRegionHeight() / 2.0f,
    keyFrame.getRegionWidth(), keyFrame.getRegionHeight(),
    keyFrame.getRegionHeight() / (float) keyFrame.getRegionWidth(),
    2 - (keyFrame.getRegionHeight() / (float) keyFrame
      .getRegionWidth()), rotation, false);

Plane Game Pitch Corrected