如何使用 Pygame 围绕其中心旋转图像? [英] How do I rotate an image around its center using Pygame?
问题描述
我一直在尝试使用 pygame.transform.rotate()
围绕其中心旋转图像,但它不起作用.具体挂起的部分是rot_image = rot_image.subsurface(rot_rect).copy()
.我得到了例外:
ValueError: 次表面矩形外表面积
这是用于旋转图像的代码:
def rot_center(图像,角度):"""旋转图像,同时保持其中心和大小"""orig_rect = image.get_rect()rot_image = pygame.transform.rotate(图像,角度)rot_rect = orig_rect.copy()rot_rect.center = rot_image.get_rect().centerrot_image = rot_image.subsurface(rot_rect).copy()返回 rot_image
简答:
获取原图的矩形并设置位置.获取旋转图像的矩形,并通过原始矩形的中心设置中心位置.返回旋转图像和矩形的元组:
def rot_center(image, angle, x, y):旋转图像 = pygame.transform.rotate(图像,角度)new_rect =rotated_image.get_rect(center = image.get_rect(center = (x, y)).center)返回rotated_image, new_rect
或者编写一个旋转和.blit
图像的函数:
def blitRotateCenter(surf, image, topleft, angle):旋转图像 = pygame.transform.rotate(图像,角度)new_rect =rotated_image.get_rect(center = image.get_rect(topleft = topleft).center)surf.blit(rotated_image, new_rect)
长答案:
对于以下示例和说明,我将使用由渲染文本生成的简单图像:
font = pygame.font.SysFont('Times New Roman', 50)text = font.render('图像', False, (255, 255, 0))图像 = 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))
一张图片(
这是原因,因为旋转图像的边界矩形总是大于原始图像的边界矩形(除了某些旋转 90 度的倍数).
由于多次复制,图像会失真.每次旋转都会产生一个小错误(不准确).误差的总和在增加,图像在衰减.
这可以通过保留原始图像和blit"来解决.由单次旋转操作生成的图像形成原始图像.
angle = 0虽然没有完成:# [...]旋转图像 = pygame.transform.rotate(图像,角度)角度 += 1screen.blit(rotated_image, pos)pygame.display.flip()
现在图像似乎可以任意改变其位置,因为图像的大小随旋转而变化,原点始终是图像边界矩形的左上角.
这可以通过比较旋转前图像的
甚至可以在原始图像上定义一个枢轴.翻译"必须计算相对于图像左上角的枢轴的blit"和blit".图像的位置必须通过翻译进行移位.
定义一个枢轴,例如在图像的中心:
pivot = pygame.math.Vector2(w/2, -h/2)
计算旋转轴的平移:
pivot_rotate = pivot.rotate(angle)枢轴移动 = 枢轴旋转 - 枢轴
最后计算旋转图像的原点:
origin = (pos[0] + min_box[0] - pivot_move[0], pos[1] - max_box[1] + pivot_move[1])旋转图像 = pygame.transform.rotate(图像,角度)screen.blit(rotated_image,原点)
在下面的示例程序中,函数 blitRotate(surf, image, pos, originPos, angle)
完成了上述所有步骤,blit"旋转到表面的图像.
surf
是目标 Surface
的表面image
是需要旋转和blit
pos
是pivot在目标Surface上的位置surf
(相对于surf
的左上角)>originPos
是枢轴在image
表面上的位置(相对于image
的左上角)angle
是以度为单位的旋转角度
这意味着,blitRotate
的第二个参数 (pos
) 是窗口中枢轴点的位置,第三个参数 (originPos
>) 是旋转表面上枢轴点的位置:
最小示例:
导入pygamepygame.init()屏幕 = pygame.display.set_mode((300, 300))时钟 = pygame.time.Clock()def blitRotate(surf, image, pos, originPos, angle):# 计算旋转图像的轴对齐边界框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])# 计算枢轴的平移枢轴 = pygame.math.Vector2(originPos[0], -originPos[1])枢轴旋转 = 枢轴.旋转(角度)枢轴移动 = 枢轴旋转 - 枢轴# 计算旋转图像的左上角原点origin = (pos[0] - originPos[0] + min_box[0] - pivot_move[0], pos[1] - originPos[1] - max_box[1] + pivot_move[1])# 获取旋转图像旋转图像 = pygame.transform.rotate(图像,角度)# 旋转和 blit 图像surf.blit(rotated_image, origin)# 在图像周围绘制矩形pygame.draw.rect(surf, (255, 0, 0), (*origin, *rotated_image.get_size()),2)def blitRotate2(冲浪,图像,左上角,角度):旋转图像 = pygame.transform.rotate(图像,角度)new_rect =rotated_image.get_rect(center = image.get_rect(topleft = topleft).center)surf.blit(rotated_image, new_rect.topleft)pygame.draw.rect(surf, (255, 0, 0), new_rect, 2)尝试:image = pygame.image.load('AirPlaneFront1-128.png')除了:text = pygame.font.SysFont('Times New Roman', 50).render('image', False, (255, 255, 0))图像 = 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()开始=假角度 = 0完成 = 错误虽然没有完成:时钟滴答(60)对于 pygame.event.get() 中的事件:如果 event.type == pygame.QUIT:完成 = 真elif event.type == pygame.KEYDOWN 或 event.type == pygame.MOUSEBUTTONDOWN:开始 = 真pos = (screen.get_width()/2, screen.get_height()/2)屏幕填充(0)blitRotate(屏幕,图像,位置,(w/2,h/2),角度)#blitRotate2(屏幕,图像,位置,角度)如果开始:角度 += 1pygame.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()出口()
另见旋转表面 和问题的答案:
I had been trying to rotate an image around its center in using pygame.transform.rotate()
but it's not working. Specifically the part that hangs is rot_image = rot_image.subsurface(rot_rect).copy()
. I get the exception:
ValueError: subsurface rectangle outside surface area
Here is the code used to rotate an image:
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
Short answer:
Get the rectangle of the original image and set the position. Get the rectangle of the rotated image and set the center position through the center of the original rectangle. Return a tuple of the rotated image and the rectangle:
def rot_center(image, angle, x, y):
rotated_image = pygame.transform.rotate(image, angle)
new_rect = rotated_image.get_rect(center = image.get_rect(center = (x, y)).center)
return rotated_image, new_rect
Or write a function which rotates and .blit
the image:
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)
Long answer:
For the following examples and explanation I'll use a simple image generated by a rendered text:
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))
An image (pygame.Surface
) can be rotated by pygame.transform.rotate
.
If that is done progressively in a loop, then the image gets distorted and rapidly increases:
while not done:
# [...]
image = pygame.transform.rotate(image, 1)
screen.blit(image, pos)
pygame.display.flip()
This is cause, because the bounding rectangle of a rotated image is always greater than the bounding rectangle of the original image (except some rotations by multiples of 90 degrees).
The image gets distort because of the multiply copies. Each rotation generates a small error (inaccuracy). The sum of the errors is growing and the images decays.
That can be fixed by keeping the original image and "blit" an image which was generated by a single rotation operation form the original image.
angle = 0
while not done:
# [...]
rotated_image = pygame.transform.rotate(image, angle)
angle += 1
screen.blit(rotated_image, pos)
pygame.display.flip()
Now the image seems to arbitrary change its position, because the size of the image changes by the rotation and origin is always the top left of the bounding rectangle of the image.
This can be compensated by comparing 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:
w, h = image.get_size()
box = [pygame.math.Vector2(p) for p in [(0, 0), (w, 0), (w, -h), (0, -h)]]
Rotate the vectors to the corner points by pygame.math.Vector2.rotate
:
box_rotate = [p.rotate(angle) for p in box]
Get the minimum and the maximum of the rotated points:
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 "compensated" origin of the upper left point of the image by adding the minimum of the rotated box to the position. For the y coordinate max_box[1]
is the minimum, because of the "flipping" along the y axis:
origin = (pos[0] + min_box[0], pos[1] - max_box[1])
rotated_image = pygame.transform.rotate(image, angle)
screen.blit(rotated_image, origin)
It is even possible to define a pivot on the original image. The "translation" of the pivot in relation to the upper left of the image has to be calculated and the "blit" position of the image has to be displaced by the translation.
Define a pivot e.g. in the center of the image:
pivot = pygame.math.Vector2(w/2, -h/2)
Calculate the translation of the rotated pivot:
pivot_rotate = pivot.rotate(angle)
pivot_move = pivot_rotate - pivot
Finally calculate the origin of the rotated image:
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)
In the following example program, the function blitRotate(surf, image, pos, originPos, angle)
does all the above steps and "blit" a rotated image to a surface.
surf
is the target Surfaceimage
is the Surface which has to be rotated andblit
pos
is the position of the pivot on the target Surfacesurf
(relative to the top left ofsurf
)originPos
is position of the pivot on theimage
Surface (relative to the top left ofimage
)angle
is the angle of rotation in degrees
This means, the 2nd argument (pos
) of blitRotate
is the position of the pivot point in the window and the 3rd argument (originPos
) is the position of the pivot point on the rotating Surface:
Minimal example:
import pygame
pygame.init()
screen = pygame.display.set_mode((300, 300))
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)
def blitRotate2(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)
pygame.draw.rect(surf, (255, 0, 0), new_rect, 2)
try:
image = pygame.image.load('AirPlaneFront1-128.png')
except:
text = pygame.font.SysFont('Times New Roman', 50).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()
start = False
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 or event.type == pygame.MOUSEBUTTONDOWN:
start = True
pos = (screen.get_width()/2, screen.get_height()/2)
screen.fill(0)
blitRotate(screen, image, pos, (w/2, h/2), angle)
#blitRotate2(screen, image, pos, angle)
if start:
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()
exit()
See also Rotate surface and the answers to the questions:
- How can you rotate an image around an off center pivot in Pygame
- How to rotate an image around its center while its scale is getting larger(in Pygame)
这篇关于如何使用 Pygame 围绕其中心旋转图像?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!