如何在 Pygame 中围绕偏离中心的枢轴旋转图像
- 2025-03-26 09:10:00
- admin 原创
- 10
问题描述:
我想围绕一个枢轴旋转图像,该枢轴不在Pygame中的Surface中心。
枢轴是图像中的绿色十字:
我知道游戏窗口中枢轴的位置。我如何跟踪该点处的图像并同时围绕该点旋转它。
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:
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 中的 y 轴向下,因此必须通过找到旋转倒置高度的最大值(“ max(rotate(-h)) ”)pygame.math.Vector2.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)
旋转并blit图像:
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
是目标 Surface 上枢轴的位置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()
相同的算法也可用于Sprite。
在这种情况下,位置 ( self.pos
)、枢轴 ( self.pivot
) 和角度 ( self.angle
) 是类的实例属性。在update
方法中,计算self.rect
和属性。self.image
最小示例: repl.it/@Rabbid76/PyGame-RotateSpriteAroundOffCenterPivot
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 中)