Как вы можете повернуть изображение вокруг центра поворота в Pygame

avatar
Rabbid76
25 января 2020 в 14:01
1088
1
4

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

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)

avatar
Rabbid76
25 января 2020 в 14:01
9

Сначала необходимо определить положение поворота на Surface:

image = pygame.image.load("boomerang64.png") # 64x64 surface
pivot = (48, 21)                             # position of the pivot on the image

Когда изображение поворачивается, его размер увеличивается. Мы должны сравнить выровненную по оси ограничивающую рамку изображения до поворота и после поворота.
Для следующей математики используется pygame.math.Vector2. Обратите внимание, что в координатах экрана y указывает вниз по экрану, но точки математической оси y образуют снизу вверх. Это приводит к тому, что ось Y должна быть "перевернута" во время вычислений. Обратите внимание, что pygame.math.Vector2.rotate вращается в направлении, противоположном направлению pygame.transform.rotate. Следовательно, угол должен быть инвертирован.

Создайте список с 4 угловыми точками ограничивающего прямоугольника и поверните векторы к угловым точкам на pygame.math.Vector2.rotate. Наконец, найдите минимум повернутой коробки. Поскольку ось y в pygame направлена ​​вниз, это необходимо компенсировать путем нахождения максимума повернутой перевернутой высоты (" max (rotate (-h)) "):

Вычислить вектор от центра изображения до точки поворота:

image_rect = image.get_rect(topleft = (pos[0] - originPos[0], pos[1]-originPos[1]))
offset_center_to_pivot = pygame.math.Vector2(pos) - image_rect.center

Повернуть вектор смещения:

rotated_offset = offset_center_to_pivot.rotate(-angle)

Вычислить центр повернутого изображения:

rotated_image_center = (pos[0] - rotated_offset.x, pos[1] - rotated_offset.y)

Повернуть и свернуть изображение:

rotated_image = pygame.transform.rotate(image, angle)
rotated_image_rect = rotated_image.get_rect(center = rotated_image_center)

screen.blit(rotated_image, rotated_image_rect)

В следующем примере программы функция blitRotate(surf, image, pos, originPos, angle) выполняет все вышеперечисленные шаги, а blit - повернутое изображение на поверхности, которое связано с дисплеем:

  • surf - целевая поверхность
  • image - Поверхность, которую нужно повернуть, а blit
  • pos - позиция точки поворота на целевой поверхности surf (относительно верхнего левого угла surf)
  • originPos - положение точки поворота на image поверхности (относительно верхнего левого угла image)
  • angle - угол поворота в градусах

import math
import pygame

def blitRotate(surf, image, pos, originPos, angle):
    image_rect = image.get_rect(topleft = (pos[0] - originPos[0], pos[1]-originPos[1]))
    offset_center_to_pivot = pygame.math.Vector2(pos) - image_rect.center
    rotated_offset = offset_center_to_pivot.rotate(-angle)
    rotated_image_center = (pos[0] - rotated_offset.x, pos[1] - rotated_offset.y)
    rotated_image = pygame.transform.rotate(image, angle)
    rotated_image_rect = rotated_image.get_rect(center = rotated_image_center)
    surf.blit(rotated_image, rotated_image_rect)
  
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.draw.circle(screen, (0, 255, 0), pos, 7, 0)

    pygame.display.flip()
    frame += 1
    angle += 10
    
pygame.quit()
exit()

Тот же алгоритм можно использовать и для спрайта.
В этом случае позиция (self.pos), точка поворота (self.pivot) и угол (self.angle) являются атрибутами экземпляра класса. В методе update вычисляются атрибуты self.rect и self.image.

Минимальный пример: repl.it/@Rabbid76/PyGame-RotateSpriteAroundOffCenterPivot <40002084468

import math
import pygame

class SpriteRotate(pygame.sprite.Sprite):

    def __init__(self, imageName, origin, pivot):
        super().__init__() 
        self.image = pygame.image.load(imageName)
        self.original_image = self.image
        self.rect = self.image.get_rect(topleft = (origin[0]-pivot[0], origin[1]-pivot[1]))
        self.origin = origin
        self.pivot = pivot
        self.angle = 0

    def update(self):
        
        # offset from pivot to center
        image_rect = self.original_image.get_rect(topleft = (self.origin[0] - self.pivot[0], self.origin[1]-self.pivot[1]))
        offset_center_to_pivot = pygame.math.Vector2(self.origin) - image_rect.center
        
        # roatated offset from pivot to center
        rotated_offset = offset_center_to_pivot.rotate(-self.angle)

        # roatetd image center
        rotated_image_center = (self.pos[0] - rotated_offset.x, self.pos[1] - rotated_offset.y)

        # get a rotated image
        self.image  = pygame.transform.rotate(self.original_image, self.angle)
        self.rect   = self.image.get_rect(center = rotated_image_center)
        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.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.draw.circle(screen, (0, 255, 0), pos, 7, 0)

    pygame.display.flip()
    frame += 1
    
pygame.quit()
exit()

Как повернуть изображение вокруг его центра с помощью Pygame?
Как повернуть изображение вокруг его центра при увеличении его масштаба (в Pygame) >