Как вы можете повернуть изображение вокруг смещенного центра в Pygame? - PullRequest
3 голосов
/ 25 января 2020

Я хочу повернуть изображение вокруг оси, которая находится не в центре Поверхность в Пигмея .
Центром является зеленый крест на изображении:

enter image description here

Я знаю положение разворота в окне игры. Как мне отследить изображение в этой точке и одновременно повернуть его вокруг этой точки.

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

    # [...]

1 Ответ

7 голосов
/ 25 января 2020

Сначала необходимо определить положение оси на 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)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...