Сначала необходимо определить положение оси на Surface
:
image = pygame.image.load("boomerang64.png2") # 64x64 surface
pivot = (48, 21) # position of the pivot on the image
Когда изображение поворачивается, его размер увеличивается. Нам нужно сравнить ограничивающий прямоугольник с выравниванием по оси изображения до вращения и после вращения.
Для следующей математики используется pygame.math.Vector2
. Обратите внимание на координаты экрана, точки y вниз по экрану, но точки математической оси y образуют нижнюю часть к верхней. Это приводит к тому, что ось y должна быть «перевернута» во время вычислений
Создайте список с 4 угловыми точками ограничительной рамки и поверните векторы в угловые точки на pygame.math.Vector2.rotate
. Наконец, найдите минимум повернутой коробки. Поскольку ось Y в Pygame указывает вниз, это должно быть компенсировано путем нахождения максимума повернутой перевернутой высоты (« 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]
Вычисление min_x
и min_y
может быть улучшено прямым вычислением компонента x и y повернутых векторов с помощью trigonometri c функций:
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])
Рассчитать «компенсированное» начало верхней левой точки изображения путем добавления минимума повернутого прямоугольника к положению относительно оси на изображении:
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)
В следующем примере программы функция blitRotate(surf, image, pos, originPos, angle)
выполняет все вышеперечисленные шаги и blit
поворачивает изображение на поверхность, которая связана с дисплеем:
surf
является целевой поверхностью image
- Поверхность, которую необходимо повернуть, а blit
pos
- это положение поворота на целевой поверхности. surf
(относительно верхнего левого угла surf
) originPos
- позиция разворота на й e image
Поверхность (относительно верхнего левого угла image
) angle
- угол поворота в градусах
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()
Тот же алгоритм можно использовать и для Sprite .
В этом случае позиция (self.pos
), разворот (self.pivot
) и угол (self.angle
) являются атрибутами экземпляра класса. В методе update
вычисляются атрибуты self.rect
и self.image
. Например:
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()
См. также
Как повернуть изображение вокруг его центра с помощью Pygame?
Как повернуть изображение вокруг его центра, в то время как его масштаб увеличивается (в Pygame)