+-
python – 如何使用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
最佳答案
简短回答:

存储源图像矩形的中心并在旋转后更新旋转图像矩形的中心,通过存储的中心位置并返回旋转图像和矩形的元组:

def rot_center(image, angle):

    center = image.get_rect().center
    rotated_image = pygame.transform.rotate(image, angle)
    new_rect = rotated_image.get_rect(center = 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.topleft)

答案很长:

对于以下示例和说明,我将使用由渲染文本生成的简单图像:

font = pygame.font.SysFont('Times New Roman', 50)
text = font.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))

图像(pygame.Surface)可以旋转pygame.transform.rotate.

如果在循环中逐步完成,则图像会失真并迅速增加:

while not done:

    # [...]

    image = pygame.transform.rotate(image, 1)
    screen.blit(image, pos)
    pygame.display.flip()

这是原因,因为旋转图像的边界矩形总是大于原始图像的边界矩形(除了一些旋转90度的倍数).
由于复制副本,图像变形.每次旋转都会产生一个小错误(不准确).错误的总和正在增长,图像也会衰减.

这可以通过保持原始图像并且“blit”通过从原始图像的单个旋转操作生成的图像来修复.

angle = 0
while not done:

    # [...]

    rotated_image = pygame.transform.rotate(image, angle)
    angle += 1

    screen.blit(rotated_image, pos)
    pygame.display.flip()

现在图像似乎随意改变其位置,因为图像的大小会因旋转而改变,而原点始终是图像边界矩形的左上角.

这可以通过比较旋转前和旋转后的图像的axis aligned bounding box来补偿.
对于以下数学,使用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)

甚至可以在原始图像上定义枢轴.必须计算枢轴相对于图像左上方的“平移”,并且图像的“blit”位置必须通过平移来移位.

定义一个例如在图像的中心:

pivot = pygame.math.Vector2(w/2, -h/2)

计算旋转轴的平移:

pivot_rotate = pivot.rotate(angle)
pivot_move   = pivot_rotate - pivot

最后计算旋转图像的原点:

origin = (pos[0] + min_box[0] - pivot_move[0], pos[1] - max_box[1] + pivot_move[1])

rotated_image = pygame.transform.rotate(image, angle)
screen.blit(rotated_image, origin)

在下面的示例程序中,函数blitRotate执行上述所有步骤,并将旋转图像“blit”到曲面. pos是图像的位置. originPos是放置在pos和pivot上的图像上的点:

import pygame
import pygame.font

pygame.init()
size = (400,400)
screen = pygame.display.set_mode(size)
clock = pygame.time.Clock()

def blitRotate(surf, image, pos, originPos, angle):

    # calcaulate the axis aligned bounding box of the rotated image
    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_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])

    # 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_box[0] - pivot_move[0], pos[1] - originPos[1] - max_box[1] + pivot_move[1])

    # get a rotated image
    rotated_image = pygame.transform.rotate(image, angle)

    # rotate and blit the image
    surf.blit(rotated_image, origin)

    # draw rectangle around the image
    pygame.draw.rect (surf, (255, 0, 0), (*origin, *rotated_image.get_size()),2)

font = pygame.font.SysFont('Times New Roman', 50)
text = font.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
        elif event.type == pygame.KEYDOWN:
            if event.key==pygame.K_ESCAPE:
                done = True

    pos = (screen.get_width()/2, screen.get_height()/2)
    pos = (200, 200)

    screen.fill(0)
    blitRotate(screen, image, pos, (w/2, h/2), 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()
点击查看更多相关文章

转载注明原文:python – 如何使用Pygame围绕其中心旋转图像? - 乐贴网