如何在 Pygame 中围绕偏离中心的支点旋转图像 [英] How can you rotate an image around an off center pivot in Pygame

查看:63
本文介绍了如何在 Pygame 中围绕偏离中心的支点旋转图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想围绕一个枢轴旋转图像,该枢轴不在

我知道游戏窗口中枢轴的位置.我如何在此时跟踪图像并同时围绕该点旋转它.

image = pygame.image.load("boomerang64.png")位置 = (200, 200)角度 = 0为真:# [...]rotate_rect,rotate_image = ????# 围绕绿色十字按角度旋转surf.blit(rotated_image,rotate_rect)角度 += 1# [...]

解决方案

首先必须定义 Surface 上枢轴的位置:

image = pygame.image.load("boomerang64.png") # 64x64 表面枢轴 = (48, 21) # 图像上枢轴的位置

旋转图像时,其大小会增加.我们必须比较旋转前和旋转后图像的

导入pygame导入数学def blitRotate(surf, image, pos, originPos, angle):# 计算旋转图像的轴对齐边界框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])# 计算枢轴的平移枢轴 = pygame.math.Vector2(originPos[0], -originPos[1])枢轴旋转 = 枢轴.旋转(角度)枢轴移动 = 枢轴旋转 - 枢轴# 计算旋转图像的左上角原点origin = (pos[0] - originPos[0] + min_x - pivot_move[0], pos[1] - originPos[1] - min_y + pivot_move[1])# 获取旋转图像旋转图像 = pygame.transform.rotate(图像,角度)# 旋转和 blit 图像surf.blit(rotated_image, origin)pygame.init()大小 = (400,400)屏幕 = pygame.display.set_mode(size)时钟 = pygame.time.Clock()image = pygame.image.load('boomerang64.png')枢轴 = (48, 21)角度,框架 = 0, 0完成 = 错误虽然没有完成:时钟滴答(60)对于 pygame.event.get() 中的事件:如果 event.type == pygame.QUIT:完成 = 真屏幕填充(0)pos = (200 + math.cos(frame * 0.05)*100, 200 + math.sin(frame * 0.05)*50)blitRotate(屏幕、图像、位置、枢轴、角度)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()帧 += 1角度 += 10pygame.quit()


相同的算法可用于

导入pygame导入数学类 SpriteRotate(pygame.sprite.Sprite):def __init__(self, imageName, pos, pivot):super().__init__()self.image = pygame.image.load(imageName)self.original_image = self.imageself.rect = self.image.get_rect(topleft = (pos[0]-pivot[0], pos[1]-pivot[1]))self.pos = posself.pivot = 枢轴self.angle = 0定义更新(自我):# 计算旋转图像的轴对齐边界框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])# 计算枢轴的平移枢轴 = pygame.math.Vector2(self.pivot[0], -self.pivot[1])pivot_rotate = pivot.rotate(self.angle)枢轴移动 = 枢轴旋转 - 枢轴# 计算旋转图像的左上角原点origin = (self.pos[0] - self.pivot[0] + min_x - pivot_move[0], self.pos[1] - self.pivot[1] - min_y + pivot_move[1])# 获取旋转图像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 += 10pygame.init()大小 = (400,400)屏幕 = pygame.display.set_mode(size)时钟 = pygame.time.Clock()回旋镖 = SpriteRotate('boomerang64.png', (200, 200), (48, 21))all_sprites = pygame.sprite.Group(回旋镖)帧 = 0完成 = 错误虽然没有完成:时钟滴答(60)对于 pygame.event.get() 中的事件:如果 event.type == pygame.QUIT:完成 = 真pos = (200 + math.cos(frame * 0.05)*100, 200 + math.sin(frame * 0.05)*50)回旋镖.pos = posall_sprites.update()屏幕填充(0)all_sprites.draw(屏幕)pygame.display.flip()帧 += 1pygame.quit()


如何旋转图像它的中心使用 Pygame?
如何围绕其中心旋转图像,同时其比例变大(在 Pygame 中)

I want to rotate an image around a pivot, which is not in the center of the Surface in Pygame.
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:

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. Note in screen coordinates the y points down the screen, but the mathematical y axis points form the bottom to the top. This causes that the y axis has to be "flipped" during calculations

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. 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))"):

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:

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:

  • surf is the target Surface
  • image is the Surface which has to be rotated and blit
  • pos is the position of the pivot on the target Surface surf (relative to the top left of surf)
  • originPos is position of the pivot on the image Surface (relative to the top left of image)
  • angle is the angle of rotation in degrees

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.
In that case the position (self.pos), pivot (self.pivot) and angle (self.angle) are instance attributes of the class. In the update method the self.rect and self.image attributes are computed.

Minimal example:

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?
How to rotate an image around its center while its scale is getting larger(in Pygame)

这篇关于如何在 Pygame 中围绕偏离中心的支点旋转图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆