I want to rotate an image around a pivot, which is not in the center of the Surface in Pygame .
The pivot is the green cross in the image:
I know the position of the pivot in the game window. How do I track the image at this point and I rotate it around this point simultaneously.
image = pygame.image.load("boomerang64.png")
pos = (200, 200)
angle = 0
while True:
# [...]
rotate_rect, rotate_image = ???? # rotate around green cross by angle
surf.blit(rotated_image, rotate_rect)
angle += 1
# [...]
First the position of the pivot on the Surface
has to be defined:
image = pygame.image.load("boomerang64.png") # 64x64 surface
pivot = (48, 21) # position of the pivot on the image
When an image is rotated, then its size increase. We have to compare the axis aligned bounding box of the image before the rotation and after the rotation.
For the following math pygame.math.Vector2
is used. Note in screen coordinates the y points down the screen, but the mathematical y axis points form the bottom to the top. This causes that the y axis has to be "flipped" during calculations
Set up a list with the 4 corner points of the bounding box and rotate the vectors to the corner points by pygame.math.Vector2.rotate
. Finally find the minimum of the rotated box. Since the y axis in pygame points downwards, this has to be compensated by finding the maximum of the rotated inverted height (" max(rotate(-h)) "):
w, h = image.get_size()
box = [pygame.math.Vector2(p) for p in [(0, 0), (w, 0), (w, -h), (0, -h)]]
box_rotate = [p.rotate(angle) for p in box]
min_x, min_y = min(box_rotate, key=lambda p: p[0])[0], max(box_rotate, key=lambda p: p[1])[1]
The computation of min_x
and min_y
can be improved by directly computing the x and y component of the rotated vectors by trigonometric functions:
w, h = image.get_size()
sin_a, cos_a = math.sin(math.radians(angle)), math.cos(math.radians(angle))
min_x, min_y = min([0, sin_a*h, cos_a*w, sin_a*h + cos_a*w]), max([0, sin_a*w, -cos_a*h, sin_a*w - cos_a*h])
Calculate the "compensated" origin of the upper left point of the image by adding the minimum of the rotated box to the position in relation to the pivot on the image:
origin = (pos[0] - originPos[0] + min_x - pivot_move[0], pos[1] - originPos[1] - min_y + pivot_move[1])
rotated_image = pygame.transform.rotate(image, angle)
screen.blit(rotated_image, origin)
In the following example program, the function blitRotate(surf, image, pos, originPos, angle)
does all the above steps and blit
a rotated image to the Surface which is associated to the display:
surf
is the target Surface image
is the Surface which has to be rotated and blit
pos
is the position of the pivot on the target Surface surf
(relative to the top left of surf
) originPos
is position of the pivot on the image
Surface (relative to the top left of image
) angle
is the angle of rotation in degrees import pygame
import math
def blitRotate(surf, image, pos, originPos, angle):
# calcaulate the axis aligned bounding box of the rotated image
w, h = image.get_size()
sin_a, cos_a = math.sin(math.radians(angle)), math.cos(math.radians(angle))
min_x, min_y = min([0, sin_a*h, cos_a*w, sin_a*h + cos_a*w]), max([0, sin_a*w, -cos_a*h, sin_a*w - cos_a*h])
# calculate the translation of the pivot
pivot = pygame.math.Vector2(originPos[0], -originPos[1])
pivot_rotate = pivot.rotate(angle)
pivot_move = pivot_rotate - pivot
# calculate the upper left origin of the rotated image
origin = (pos[0] - originPos[0] + min_x - pivot_move[0], pos[1] - originPos[1] - min_y + pivot_move[1])
# get a rotated image
rotated_image = pygame.transform.rotate(image, angle)
# rotate and blit the image
surf.blit(rotated_image, origin)
pygame.init()
size = (400,400)
screen = pygame.display.set_mode(size)
clock = pygame.time.Clock()
image = pygame.image.load('boomerang64.png')
pivot = (48, 21)
angle, frame = 0, 0
done = False
while not done:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill(0)
pos = (200 + math.cos(frame * 0.05)*100, 200 + math.sin(frame * 0.05)*50)
blitRotate(screen, image, pos, pivot, angle)
pygame.draw.line(screen, (0, 255, 0), (pos[0]-20, pos[1]), (pos[0]+20, pos[1]), 3)
pygame.draw.line(screen, (0, 255, 0), (pos[0], pos[1]-20), (pos[0], pos[1]+20), 3)
pygame.display.flip()
frame += 1
angle += 10
pygame.quit()
The same algorithm can be used for a Sprite , too.
In that case the position ( self.pos
), pivot ( self.pivot
) and angle ( self.angle
) are instance attributes of the class. In the update
method the self.rect
and self.image
attributes are computed.
Minimal example: repl.it/@Rabbid76/PyGame-RotateSpriteAroundOffCenterPivot
import pygame
import math
class SpriteRotate(pygame.sprite.Sprite):
def __init__(self, imageName, pos, pivot):
super().__init__()
self.image = pygame.image.load(imageName)
self.original_image = self.image
self.rect = self.image.get_rect(topleft = (pos[0]-pivot[0], pos[1]-pivot[1]))
self.pos = pos
self.pivot = pivot
self.angle = 0
def update(self):
# calcaulate the axis aligned bounding box of the rotated image
w, h = self.original_image.get_size()
sin_a, cos_a = math.sin(math.radians(self.angle)), math.cos(math.radians(self.angle))
min_x, min_y = min([0, sin_a*h, cos_a*w, sin_a*h + cos_a*w]), max([0, sin_a*w, -cos_a*h, sin_a*w - cos_a*h])
# calculate the translation of the pivot
pivot = pygame.math.Vector2(self.pivot[0], -self.pivot[1])
pivot_rotate = pivot.rotate(self.angle)
pivot_move = pivot_rotate - pivot
# calculate the upper left origin of the rotated image
origin = (self.pos[0] - self.pivot[0] + min_x - pivot_move[0], self.pos[1] - self.pivot[1] - min_y + pivot_move[1])
# get a rotated image
self.image = pygame.transform.rotate(self.original_image, self.angle)
self.rect = self.image.get_rect(topleft = (round(origin[0]), round(origin[1])))
self.angle += 10
pygame.init()
size = (400,400)
screen = pygame.display.set_mode(size)
clock = pygame.time.Clock()
boomerang = SpriteRotate('boomerang64.png', (200, 200), (48, 21))
all_sprites = pygame.sprite.Group(boomerang)
frame = 0
done = False
while not done:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
pos = (200 + math.cos(frame * 0.05)*100, 200 + math.sin(frame * 0.05)*50)
boomerang.pos = pos
all_sprites.update()
screen.fill(0)
all_sprites.draw(screen)
pygame.display.flip()
frame += 1
pygame.quit()
How do I rotate an image around its center using Pygame?
How to rotate an image around its center while its scale is getting larger(in Pygame)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.