简体   繁体   English

如何在 Pygame 中围绕偏离中心的支点旋转图像

[英]How can you rotate an image around an off center pivot in Pygame

I want to rotate an image around a pivot, which is not in the center of the Surface in Pygame .我想围绕一个枢轴旋转图像,该枢轴不在Pygame 中Surface的中心。
The pivot is the green cross in the image:枢轴是图像中的绿色十字:

在此处输入图片说明

I know the position of the pivot in the game window.我知道游戏窗口中枢轴的位置。 How do I track the image at this point and I rotate it around this point simultaneously.我如何在此时跟踪图像并同时围绕该点旋转它。

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

    # [...]

First the position of the pivot on the Surface has to be defined:首先必须定义Surface上枢轴的位置:

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

When an image is rotated, then its size increase.旋转图像时,其大小会增加。 We have to compare the axis aligned bounding box of the image before the rotation and after the rotation.我们必须比较旋转前和旋转后图像的轴对齐边界框
For the following math pygame.math.Vector2 is used.对于以下数学,使用pygame.math.Vector2 Note in screen coordinates the y points down the screen, but the mathematical y axis points form the bottom to the top.注意屏幕坐标中的 y 指向屏幕下方,但数学 y 轴点从底部到顶部。 This causes that the y axis has to be "flipped" during calculations这会导致在计算过程中必须“翻转”y 轴

Set up a list with the 4 corner points of the bounding box and rotate the vectors to the corner points by pygame.math.Vector2.rotate .使用边界框的 4 个角点设置一个列表,并通过pygame.math.Vector2.rotate将向量旋转到角点。 Finally find the minimum of the rotated box.最后找到旋转框的最小值。 Since the y axis in pygame points downwards, this has to be compensated by finding the maximum of the rotated inverted height (" max(rotate(-h)) "):由于 pygame 中的 y 轴指向下方,因此必须通过找到旋转倒置高度的最大值(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]

The computation of min_x and min_y can be improved by directly computing the x and y component of the rotated vectors by trigonometric functions: min_xmin_y的计算可以通过三角函数直接计算旋转向量的 x 和 y 分量来改进:

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 "compensated" origin of the upper left point of the image by adding the minimum of the rotated box to the position in relation to the pivot on the image:通过将旋转框的最小值添加到与图像上的枢轴相关的位置,计算图像左上点的“补偿”原点:

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)

In the following example program, the function blitRotate(surf, image, pos, originPos, angle) does all the above steps and blit a rotated image to the Surface which is associated to the display:在以下示例程序中,函数blitRotate(surf, image, pos, originPos, angle)执行上述所有步骤并将旋转的图像blit到与显示器关联的 Surface:

  • surf is the target Surface surf是目标表面
  • image is the Surface which has to be rotated and blit image是必须旋转和blit的 Surface
  • pos is the position of the pivot on the target Surface surf (relative to the top left of surf ) pos是目标 Surface surf上的枢轴位置(相对于surf左上角)
  • originPos is position of the pivot on the image Surface (relative to the top left of image ) originPosimage Surface 上枢轴的位置(相对于image左上角)
  • angle is the angle of rotation in degrees 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()

The same algorithm can be used for a Sprite , too.同样的算法也可以用于Sprite
In that case the position ( self.pos ), pivot ( self.pivot ) and angle ( self.angle ) are instance attributes of the class.在这种情况下,位置 ( self.pos )、枢轴 ( self.pivot ) 和角度 ( self.angle ) 是类的实例属性。 In the update method the self.rect and self.image attributes are computed.update方法中计算self.rectself.image属性。

Minimal example:最小的例子: repl.it/@Rabbid76/PyGame-RotateSpriteAroundOffCenterPivot repl.it/@Rabbid76/PyGame-RotateSpriteAroundOffCenterPivot

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()

How do I rotate an image around its center using Pygame? 如何使用 Pygame 围绕其中心旋转图像?
How to rotate an image around its center while its scale is getting larger(in Pygame) 如何在图像的比例变大时围绕其中心旋转图像(在 Pygame 中)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM