Как мне повернуть изображение вокруг его центра с помощью Pygame?
Я пытался повернуть изображение вокруг его центра с помощью pygame.transform.rotate()
но это не работает. В частности, зависает часть rot_image = rot_image.subsurface(rot_rect).copy()
. Я получаю исключение:
ValueError: subsurface rectangle outside surface area
Вот код, используемый для поворота изображения:
def rot_center(image, angle):
"""rotate an image while keeping its center and size"""
orig_rect = image.get_rect()
rot_image = pygame.transform.rotate(image, angle)
rot_rect = orig_rect.copy()
rot_rect.center = rot_image.get_rect().center
rot_image = rot_image.subsurface(rot_rect).copy()
return rot_image
Переведено автоматически
Ответ 1
Краткий ответ:
При использовании pygame.transform.rotate
размер нового повернутого изображения увеличивается по сравнению с размером исходного изображения. Вы должны убедиться, что повернутое изображение размещено так, чтобы его центр оставался в центре неповращенного изображения. Для этого возьмите прямоугольник исходного изображения и установите положение. Получаем прямоугольник повернутого изображения и устанавливаем положение центра через центр исходного прямоугольника.
Возвращает кортеж из функции red_center
с повернутым изображением и ограничивающим прямоугольником повернутого изображения:
def rot_center(image, angle, x, y):
rotated_image = pygame.transform.rotate(image, angle)
new_rect = rotated_image.get_rect(center = image.get_rect(center = (x, y)).center)
return rotated_image, new_rect
Или написать функцию, которая вращает и .blit
изображение:
def blitRotateCenter(surf, image, topleft, angle):
rotated_image = pygame.transform.rotate(image, angle)
new_rect = rotated_image.get_rect(center = image.get_rect(topleft = topleft).center)
surf.blit(rotated_image, new_rect)
Длинный ответ:
Изображение (pygame.Surface
) можно повернуть с помощью pygame.transform.rotate
.
Если это делается постепенно в цикле, то изображение искажается и быстро увеличивается:
while not done:
# [...]
image = pygame.transform.rotate(image, 1)
screen.blit(image, pos)
pygame.display.flip()
Это связано с тем, что ограничивающий прямоугольник повернутого изображения всегда больше, чем ограничивающий прямоугольник исходного изображения (за исключением некоторых поворотов, кратных 90 градусам).
Изображение искажается из-за многократного копирования. Каждый поворот генерирует небольшую ошибку (неточность). Сумма ошибок растет, а изображения уменьшаются.
Это можно исправить, сохранив исходное изображение и "размыв" изображение, сгенерированное одной операцией поворота, с исходным изображением.
angle = 0
while not done:
# [...]
rotated_image = pygame.transform.rotate(image, angle)
angle += 1
screen.blit(rotated_image, pos)
pygame.display.flip()
Теперь изображение, кажется, произвольно меняет свое положение, потому что размер изображения меняется при повороте, а начало координат всегда находится в верхнем левом углу ограничивающего прямоугольника изображения.
Это можно компенсировать, сравнив выровненную по оси ограничивающую рамку изображения до поворота и после поворота.
Для следующей математики pygame.math.Vector2
используется. Примечание на экране координирует точки y вниз по экрану, но точки математической оси y образуют линию снизу вверх. Это приводит к тому, что ось y приходится "переворачивать" во время вычислений
Настройте список с 4 угловыми точками ограничивающей рамки:
w, h = image.get_size()
box = [pygame.math.Vector2(p) for p in [(0, 0), (w, 0), (w, -h), (0, -h)]]
Поверните векторы к угловым точкам на pygame.math.Vector2.rotate
:
box_rotate = [p.rotate(angle) for p in box]
Получить минимальную и максимальную из повернутых точек:
min_box = (min(box_rotate, key=lambda p: p[0])[0], min(box_rotate, key=lambda p: p[1])[1])
max_box = (max(box_rotate, key=lambda p: p[0])[0], max(box_rotate, key=lambda p: p[1])[1])
Вычислите "компенсированное" начало координат верхней левой точки изображения, добавив минимум повернутого прямоугольника к позиции. Для координаты y max_box[1]
является минимальной из-за "переворачивания" вдоль оси y:
origin = (pos[0] + min_box[0], pos[1] - max_box[1])
rotated_image = pygame.transform.rotate(image, angle)
screen.blit(rotated_image, origin)
Возможно даже определить ось поворота на исходном изображении. Вычислите вектор смещения от центра изображения к оси поворота и поверните вектор. Вектор может быть представлен с помощью pygame.math.Vector2
и может быть повернут с помощью pygame.math.Vector2.rotate
. Обратите внимание, что pygame.math.Vector2.rotate
вращается в направлении, противоположном pygame.transform.rotate
. Следовательно, угол должен быть инвертирован:
Вычислите вектор от центра изображения к оси вращения:
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)
выполняет все вышеуказанные шаги и "прикрепляет" повернутое изображение к поверхности.
surf
это целевая поверхностьimage
это поверхность, которую нужно повернуть иblit
pos
это положение оси поворота на целевой поверхностиsurf
(относительно верхнего левого углаsurf
)originPos
это положение оси поворота наimage
поверхности (относительно верхнего левого углаimage
)angle
это угол поворота в градусах
Это означает, что 2-й аргумент (pos
) из blitRotate
- это положение точки поворота в окне, а 3-й аргумент (originPos
) - это положение точки поворота на вращающейся поверхности:
Минимальный пример: repl.it/@Rabbid76/PyGame-RotateAroundPivot
import pygame
pygame.init()
screen = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
def blitRotate(surf, image, pos, originPos, angle):
# offset from pivot to center
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
# roatated offset from pivot to center
rotated_offset = offset_center_to_pivot.rotate(-angle)
# roatetd image center
rotated_image_center = (pos[0] - rotated_offset.x, pos[1] - rotated_offset.y)
# get a rotated image
rotated_image = pygame.transform.rotate(image, angle)
rotated_image_rect = rotated_image.get_rect(center = rotated_image_center)
# rotate and blit the image
surf.blit(rotated_image, rotated_image_rect)
# draw rectangle around the image
pygame.draw.rect(surf, (255, 0, 0), (*rotated_image_rect.topleft, *rotated_image.get_size()),2)
def blitRotate2(surf, image, topleft, angle):
rotated_image = pygame.transform.rotate(image, angle)
new_rect = rotated_image.get_rect(center = image.get_rect(topleft = topleft).center)
surf.blit(rotated_image, new_rect.topleft)
pygame.draw.rect(surf, (255, 0, 0), new_rect, 2)
try:
image = pygame.image.load('AirPlaneFront.png')
except:
text = pygame.font.SysFont('Times New Roman', 50).render('image', False, (255, 255, 0))
image = pygame.Surface((text.get_width()+1, text.get_height()+1))
pygame.draw.rect(image, (0, 0, 255), (1, 1, *text.get_size()))
image.blit(text, (1, 1))
w, h = image.get_size()
angle = 0
done = False
while not done:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
pos = (screen.get_width()/2, screen.get_height()/2)
screen.fill(0)
blitRotate(screen, image, pos, (w/2, h/2), angle)
#blitRotate2(screen, image, pos, angle)
angle += 1
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()
pygame.quit()
exit()
Смотрите также Поворот поверхности и ответы на вопросы:
- Как вы можете повернуть изображение вокруг смещенной от центра оси в Pygame
- Как повернуть изображение вокруг центра при увеличении его масштаба (в Pygame)
- Как повернуть изображение (проигрыватель) в направлении движения мыши?
- Как установить точку поворота (center of rotation) для pygame.transform.rotate()?
- Как направить ствол в сторону мыши в pygame?
Ответ 2
Есть некоторые проблемы с верхним ответом: позиция предыдущей прямой линии должна быть доступна в функции, чтобы мы могли назначить ее новой прямой линии, например:
rect = new_image.get_rect(center=rect.center)
В другом ответе местоположение получено путем создания нового прямоугольника из исходного изображения, но это означает, что оно будет расположено в координатах по умолчанию (0, 0).
Приведенный ниже пример должен работать корректно. Новому rect требуется center
положение старого rect, поэтому мы передаем его также в функцию. Затем поверните изображение, вызовите get_rect
, чтобы получить новую прямоугольную форму правильного размера, и передайте center
атрибут старой прямоугольной формы в качестве center
аргумента. Наконец, верните и повернутое изображение, и новый прямоугольник в виде кортежа и распакуйте его в основном цикле.
import pygame as pg
def rotate(image, rect, angle):
"""Rotate the image while keeping its center."""
# Rotate the original image without modifying it.
new_image = pg.transform.rotate(image, angle)
# Get a new rect with the center of the old rect.
rect = new_image.get_rect(center=rect.center)
return new_image, rect
def main():
clock = pg.time.Clock()
screen = pg.display.set_mode((640, 480))
gray = pg.Color('gray15')
blue = pg.Color('dodgerblue2')
image = pg.Surface((320, 200), pg.SRCALPHA)
pg.draw.polygon(image, blue, ((0, 0), (320, 100), (0, 200)))
# Keep a reference to the original to preserve the image quality.
orig_image = image
rect = image.get_rect(center=(320, 240))
angle = 0
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
angle += 2
image, rect = rotate(orig_image, rect, angle)
screen.fill(gray)
screen.blit(image, rect)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
Вот еще один пример с вращающимся спрайтом pygame.
import pygame as pg
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.angle = 0
def update(self):
self.angle += 2
self.rotate()
def rotate(self):
"""Rotate the image of the sprite around its center."""
# `rotozoom` usually looks nicer than `rotate`. Pygame's rotation
# functions return new images and don't modify the originals.
self.image = pg.transform.rotozoom(self.orig_image, self.angle, 1)
# Create a new rect with the center of the old rect.
self.rect = self.image.get_rect(center=self.rect.center)
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
all_sprites = pg.sprite.Group(Entity((320, 240)))
while True:
for event in pg.event.get():
if event.type == pg.QUIT:
return
all_sprites.update()
screen.fill((30, 30, 30))
all_sprites.draw(screen)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
Ответ 3
Вы удаляете прямоугольник, который создает rotate . Вам нужно сохранить прямоугольник, поскольку он меняет размер при повороте.
Если вы хотите сохранить расположение объектов, выполните:
def rot_center(image, angle):
"""rotate a Surface, maintaining position."""
loc = image.get_rect().center #rot_image is not defined
rot_sprite = pygame.transform.rotate(image, angle)
rot_sprite.get_rect().center = loc
return rot_sprite
# or return tuple: (Surface, Rect)
# return rot_sprite, rot_sprite.get_rect()
Ответ 4
Все, что нужно для рисования изображения в Python pygame
game_display = pygame.display.set_mode((800, 600))
x = 0
y = 0
angle = 0
img = pygame.image.load("resources/image.png")
img = pygame.transform.scale(img, (50, 50)) # image size
def draw_img(self, image, x, y, angle):
rotated_image = pygame.transform.rotate(image, angle)
game_display.blit(rotated_image, rotated_image.get_rect(center=image.get_rect(topleft=(x, y)).center).topleft)
# run this method with your loop
def tick():
draw_img(img, x, y, angle)