Image Rotation
In a previous tutorial I talked about scaling and rotating images using the transform()
method.
In this guide I’ll look at a practical application of the rotate method in a game as well as some of the pitfalls.
I’ll build a small demo that includes a turret image (below), which is rotated to point towards the mouse.
The starter code is below:
import pygame
pygame.init()
#define screen size
SCREEN_WIDTH = 1000
SCREEN_HEIGHT = 600
#create game window
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Rotating Objects")
#define colours
BG = (255, 255, 255)
BLACK = (0, 0, 0)
turret_original = pygame.image.load("turret.png").convert_alpha()
x = 400
y = 25500
#game loop
run = True
while run:
#update background
screen.fill(BG)
#rotate turret
turret = pygame.transform.rotate(turret_original, 0)
#draw image
screen.blit(turret, (x, y))
#event handler
for event in pygame.event.get():
#quit program
if event.type == pygame.QUIT:
run = False
#update display
pygame.display.flip()
pygame.quit()
In the code above we’ll begin by loading the image in on line 17, making sure to keep the original image in a separate variable to the rotated one.
Lines 18 and 19 define the x and y coordinates for blitting
the image on the screen.
On line 29 we create a rotated version of the original turret image and on line 32 we blit
the new image on the screen at the predefined x and y coordinates.
The result is a static turret positioned roughly in the middle of the screen.
Calculating The Angle
In order to calculate the required rotation angle, we need to apply a little bit of trigonometry.
The coordinates of the turret image are known and the mouse cursor coordinates can be obtained.
We can calculate the horizontal and vertical distances between the two points and using those distances, calculate the rotation angle.
First we will get the mouse coordinates. This needs to go in the main loop as we will need to readjust every time the mouse moves.
#get mouse position
pos = pygame.mouse.get_pos()
Next we calculate the x and y distances and use those to work out the angle of rotation. This requires the math
module so make sure to import that.
#calculate turret angle
x_dist = pos[0] - x
y_dist = -(pos[1] - y)#-ve because pygame y coordinates increase down the screen
angle = math.degrees(math.atan2(y_dist, x_dist))
Finally, we update the rotate method from before, to use the angle
variable.
The turret image is facing up by default, but using our calculations above, when the mouse is directly above the turret, the calculated angle would be 90 degrees. So we need to account for this difference by subtracting 90 from the calculated angle.
This will vary depending on the orientation of the image you use.
#rotate turret
turret = pygame.transform.rotate(turret_original, angle - 90)
The result however does not seem quite right. The calculations are correct, and the image does rotate but it seems to move around rather than staying still.
To visualise what is happening here, we need to show the image’s background, which is currently transparent.
To do this, we temporarily change convert_alpha()
to convert()
.
turret_original = pygame.image.load("turret.png").convert()
Now you can see what is going on a bit more clearly. As the image rotates, it gets bigger and this is what gives the impression of the turret moving around.
In reality, the turret is staying in the center of the image, but as the image gets bigger, it looks like the turret moves.
Notice that the top left corner of the image doesn’t change. That is the coordinate that we are passing into the blit
method.
To resolve this, we need to change the way the image is drawn on the screen. Rather than drawing it from the top left corner, we want to draw it from the center.
#rotate turret
turret = pygame.transform.rotate(turret_original, angle - 90)
turret_rect = turret.get_rect(center = (x, y))
#draw image
screen.blit(turret, turret_rect)
In the above code, line 3 creates a rectangle from the rotated image and positions it with the center at the x and y coordinates.
Line 6 is modified to blit
the image at the position of the rectangle.
x = 500
y = 300
We can also update the original x and y coordinates to move the image closer to the middle of the window.
Now the turret is staying in the middle as it rotates around the screen. You can still see the black background getting bigger and smaller, but since the image is being drawn from the middle rather than the corner, it doesn’t affect the turret positioning.
We can now hide the black background by changing the convert()
method back to convert_alpha()
.
And now you have a turret correctly following the mouse cursor.
The final code is:
import pygame
import math
pygame.init()
#define screen size
SCREEN_WIDTH = 1000
SCREEN_HEIGHT = 600
#create game window
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Rotating Objects")
#define colours
BG = (255, 255, 255)
BLACK = (0, 0, 0)
turret_original = pygame.image.load("turret.png").convert_alpha()
x = 500
y = 300
#game loop
run = True
while run:
#update background
screen.fill(BG)
#get mouse position
pos = pygame.mouse.get_pos()
#calculate turret angle
x_dist = pos[0] - x
y_dist = -(pos[1] - y)#-ve because pygame y coordinates increase down the screen
angle = math.degrees(math.atan2(y_dist, x_dist))
#rotate turret
turret = pygame.transform.rotate(turret_original, angle - 90)
turret_rect = turret.get_rect(center = (x, y))
#draw image
screen.blit(turret, turret_rect)
#event handler
for event in pygame.event.get():
#quit program
if event.type == pygame.QUIT:
run = False
#update display
pygame.display.flip()
pygame.quit()
Not work!
What is the error?
great but how to make it work with sprites? since sprite has its own draw(). I cant figure out how to make it blit the image at the center.