Rotating an Image using an arbitrary pivot point, with PyGame


Introduction

Rotation is everywhere, and you can use it in many, many scenarios. A ball, a falling structure, a projectile, etc.

 

The case study for this tutorial is the Blue Sword the player can use to put mobs down. As you can see the image above is the initial state, where the sword remains vertical right next to the player. When the player presses the space bar the sword starts brandishing until it completes a 170 degrees movement:

Once that position is reached, the sword starts its way back to the initial state.

Background

PyGame provides a method to rotate a surface using its center as the rotation point (Rotation axis) which we are going to rely on.

There's something you need to consider when doing this kind of transformations, in particular in this case where we want to apply it several times, it distorts the image a little bit each time, so if we preserve the rotated surface on each step we'll end up with a very noisy result.

To avoid this, we'll only preserve the original surface and apply a rotation with an increasing angle each step to that surface, discarding the previously rotated one.

Implementation

The rotation itself is pretty easy and can be done this way:

angle = 1  # In Degrees
# Create the surface
sword_surface = pygame.image.load(PATH_TO_IMAGE)
# Rotate the surface
rotated_sword_surface = pygame.transform.rotate(sword_surface, angle)
angle += 1

We will do this, for example, inside the `update` method that's called once per iteration of the main loop. So you'll need to save the value of `angle` somewhere.

This approach has a flaw, which is that the surface is being rotated using the surface's center as the rotation point, as I said before. The result looks quite weird to the eye:

To create a more "natural" movement we need to rotate the sword using a point near the sword's grip.

For this, we need to create a new vector called "rotation_vector" which starts at the pivot point and points to the surface's center. We are going to rotate this vector the same degrees as the surface, using a Vector2 instance and a Vector2's method called "rotate". Once that vector is rotated, we relocate the surface's center to the vector's X and Y.


The code looks like this:

angle = 1  # In Degrees
pivot = Vector2(p_x, p_y)  # You can use any X and Y here.
# Create the surface
sword_surface = pygame.image.load(PATH_TO_IMAGE)
sword_rect = sword_surface.get_rect()
# Create the rotation vector
rotation_vector = sword_surface.get_rect().center - pivot
# Rotate the rotation vector, and store as a new variable
rotated_vector = rotation_vector.rotate(angle)
# Rotate the surface
rotated_sword_surface = pygame.transform.rotate(sword_surface, angle)
# Relocate the surface
relocation_vector = rotated_vector - rotation_vector
sword_rect.center += relocation_vector
self.image = sword_surface
self.rect = sword_rect
angle += 1

That should do the trick!

Any feedback or correction let me know in the comments. :)

Get The Alchemist

Download NowName your own price

Leave a comment

Log in with itch.io to leave a comment.