I want to rotate a rectangle about a point other than the center. My code so far is:
import pygame
pygame.init()
w = 640
h = 480
degree = 45
screen = pygame.display.set_mode((w, h))
surf = pygame.Surface((25, 100))
surf.fill((255, 255, 255))
surf.set_colorkey((255, 0, 0))
bigger = pygame.Rect(0, 0, 25, 100)
pygame.draw.rect(surf, (100, 0, 0), bigger)
rotatedSurf = pygame.transform.rotate(surf, degree)
screen.blit(rotatedSurf, (400, 300))
running = True
while running:
event = pygame.event.poll()
if event.type == pygame.QUIT:
running = False
pygame.display.flip()
I can change the degree to get different rotation but the rotation is about the center. I want to set a point other than the center of the rectangle as the rotation point.
You must make sure that the rotated image is placed so that its center remains in the center of the non-rotated image. To do this, get the rectangle of the original image and set the position. Get the rectangle of the rotated image and set the center position through the center of the original rectangle.
Surface ) can be rotated by pygame. transform. rotate . This is cause, because the bounding rectangle of a rotated image is always greater than the bounding rectangle of the original image (except some rotations by multiples of 90 degrees).
Rotation in the polar coordinate system is as simple as moving your shape up/down, left/right in x-y coordinates. When you want to draw, you can then follow formals such as: x = cx + r * math. cos(a + rotation_angle), y = cy + r * math. sin(a + rotation_angle).
To rotate a surface around its center, we first rotate the image and then get a new rect to which we pass the center
coordinates of the previous rect to keep it centered. To rotate around an arbitrary point, we can do pretty much the same, but we also have to add an offset vector to the center position (the pivot point) to shift the rect. This vector needs to be rotated each time we rotate the image.
So we have to store the pivot point (the original center of the image or sprite) - in a tuple, list, vector or a rect - and the offset vector (the amount by which we shift the rect) and pass them to the rotate
function. Then we rotate the image and offset vector, get a new rect, pass the pivot + offset as the center
argument and finally return the rotated image and the new rect.
import pygame as pg
def rotate(surface, angle, pivot, offset):
"""Rotate the surface around the pivot point.
Args:
surface (pygame.Surface): The surface that is to be rotated.
angle (float): Rotate by this angle.
pivot (tuple, list, pygame.math.Vector2): The pivot point.
offset (pygame.math.Vector2): This vector is added to the pivot.
"""
rotated_image = pg.transform.rotozoom(surface, -angle, 1) # Rotate the image.
rotated_offset = offset.rotate(angle) # Rotate the offset vector.
# Add the offset vector to the center/pivot point to shift the rect.
rect = rotated_image.get_rect(center=pivot+rotated_offset)
return rotated_image, rect # Return the rotated image and shifted rect.
pg.init()
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
BG_COLOR = pg.Color('gray12')
# The original image will never be modified.
IMAGE = pg.Surface((140, 60), pg.SRCALPHA)
pg.draw.polygon(IMAGE, pg.Color('dodgerblue3'), ((0, 0), (140, 30), (0, 60)))
# Store the original center position of the surface.
pivot = [200, 250]
# This offset vector will be added to the pivot point, so the
# resulting rect will be blitted at `rect.topleft + offset`.
offset = pg.math.Vector2(50, 0)
angle = 0
running = True
while running:
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
keys = pg.key.get_pressed()
if keys[pg.K_d] or keys[pg.K_RIGHT]:
angle += 1
elif keys[pg.K_a] or keys[pg.K_LEFT]:
angle -= 1
if keys[pg.K_f]:
pivot[0] += 2
# Rotated version of the image and the shifted rect.
rotated_image, rect = rotate(IMAGE, angle, pivot, offset)
# Drawing.
screen.fill(BG_COLOR)
screen.blit(rotated_image, rect) # Blit the rotated image.
pg.draw.circle(screen, (30, 250, 70), pivot, 3) # Pivot point.
pg.draw.rect(screen, (30, 250, 70), rect, 1) # The rect.
pg.display.set_caption('Angle: {}'.format(angle))
pg.display.flip()
clock.tick(30)
pg.quit()
Here's a version with a pygame.sprite.Sprite
:
import pygame as pg
from pygame.math import Vector2
class Entity(pg.sprite.Sprite):
def __init__(self, pos):
super().__init__()
self.image = pg.Surface((122, 70), pg.SRCALPHA)
pg.draw.polygon(self.image, pg.Color('dodgerblue1'),
((1, 0), (120, 35), (1, 70)))
# A reference to the original image to preserve the quality.
self.orig_image = self.image
self.rect = self.image.get_rect(center=pos)
self.pos = Vector2(pos) # The original center position/pivot point.
self.offset = Vector2(50, 0) # We shift the sprite 50 px to the right.
self.angle = 0
def update(self):
self.angle += 2
self.rotate()
def rotate(self):
"""Rotate the image of the sprite around a pivot point."""
# Rotate the image.
self.image = pg.transform.rotozoom(self.orig_image, -self.angle, 1)
# Rotate the offset vector.
offset_rotated = self.offset.rotate(self.angle)
# Create a new rect with the center of the sprite + the offset.
self.rect = self.image.get_rect(center=self.pos+offset_rotated)
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
entity = Entity((320, 240))
all_sprites = pg.sprite.Group(entity)
while True:
for event in pg.event.get():
if event.type == pg.QUIT:
return
keys = pg.key.get_pressed()
if keys[pg.K_d]:
entity.pos.x += 5
elif keys[pg.K_a]:
entity.pos.x -= 5
all_sprites.update()
screen.fill((30, 30, 30))
all_sprites.draw(screen)
pg.draw.circle(screen, (255, 128, 0), [int(i) for i in entity.pos], 3)
pg.draw.rect(screen, (255, 128, 0), entity.rect, 2)
pg.draw.line(screen, (100, 200, 255), (0, 240), (640, 240), 1)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
I also had this problem and found an easy solution: You can just create a bigger surface (doubled length and doubled height) and blit the smaller surface into the bigger so that the rotation point of is the center of the bigger one. Now you can just rotate the bigger one around the center.
def rotate(img, pos, angle):
w, h = img.get_size()
img2 = pygame.Surface((w*2, h*2), pygame.SRCALPHA)
img2.blit(img, (w-pos[0], h-pos[1]))
return pygame.transform.rotate(img2, angle)
(If you would make sketch, it would make much more sense, but trust me: It works and is in my opinion easy to use and to understand than the other solutions.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With