如何在 Pygame 中围绕偏离中心的枢轴旋转图像

Posted

技术标签:

【中文标题】如何在 Pygame 中围绕偏离中心的枢轴旋转图像【英文标题】:How can you rotate an image around an off center pivot in Pygame 【发布时间】:2020-05-11 13:51:52 【问题描述】:

我想围绕一个轴旋转图像,该轴不在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

当图像旋转时,它的大小会增加。我们必须比较图像旋转前和旋转后的axis aligned bounding box。 对于以下数学,使用pygame.math.Vector2。请注意,在屏幕坐标中,y 指向屏幕下方,但数学上的 y 轴点从底部到顶部。这导致在计算期间必须“翻转”y轴。请注意,pygame.math.Vector2.rotate 的旋转方向与 pygame.transform.rotate 的旋转方向相反。因此角度必须倒转。

用边界框的4个角点建立一个列表,并将向量旋转到角点pygame.math.Vector2.rotate。最后找到旋转框的最小值。由于 pygame 中的 y 轴指向下方,因此必须通过找到旋转倒置高度的最大值来补偿这一点 ("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)

旋转和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 将旋转的图像发送到与显示器关联的 Surface:

surf 是目标表面 image 是必须旋转的表面,blit pos 是目标 Surface surf 上枢轴的位置(相对于 surf 的左上角) originPosimage 表面上的枢轴位置(相对于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.rectself.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()

How do I rotate an image around its center using Pygame?How to rotate an image around its center while its scale is getting larger(in Pygame)

【讨论】:

以上是关于如何在 Pygame 中围绕偏离中心的枢轴旋转图像的主要内容,如果未能解决你的问题,请参考以下文章

不使用 CSS3 围绕左下边缘枢轴旋转图像

如何使用 Pygame 围绕其中心旋转图像?

Android围绕动态枢轴旋转视图

如何在 3D 中围绕同一个枢轴/点旋转多个对象?

如何设置 pygame.transform.rotate() 的轴心点(旋转中心)?

颤动 - 如何使用画布围绕中心旋转图像