Python 如何使用 Pygame 围绕其中心旋转图像?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4183208/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I rotate an image around its center using Pygame?
提问by Miha
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:
我一直在尝试围绕其中心旋转图像,pygame.transform.rotate()但它不起作用。具体挂起的部分是rot_image = rot_image.subsurface(rot_rect).copy(). 我得到了例外:
ValueError: subsurface rectangle outside surface area
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
回答by Miha
Found the problem: Example works good, but needs equal dimensions for width and height. Fixed pictures and it works.
发现问题:示例效果很好,但宽度和高度需要相同的尺寸。固定图片,它的工作原理。
回答by ninMonkey
You are deleting the rect that rotate creates. You need to preserve rect, since it changes size when rotated.
您正在删除旋转创建的矩形。您需要保留 rect,因为它在旋转时会改变大小。
If you want to preserve the objects location, do:
如果要保留对象位置,请执行以下操作:
def rot_center(image, angle):
"""rotate a Surface, maintaining position."""
loc = image.get_rect().center #rot_image is not defined
rot_sprite = pygame.transform.rotate(image, angle)
rot_sprite.get_rect().center = loc
return rot_sprite
# or return tuple: (Surface, Rect)
# return rot_sprite, rot_sprite.get_rect()
回答by skrx
There are some problems with the top answer: The position of the previous rect needs to be available in the function, so that we can assign it to the new rect, e.g.:
top的回答有一些问题:函数中需要有前一个rect的位置,这样我们才能把它赋值给新的rect,例如:
rect = new_image.get_rect(center=rect.center)
In the other answer the location is obtained by creating a new rect from the original image, but that means it will be positioned at the default (0, 0) coordinates.
在另一个答案中,位置是通过从原始图像创建一个新的矩形获得的,但这意味着它将被定位在默认的 (0, 0) 坐标处。
The example below should work correctly. The new rect needs the centerposition of the old rect, so we pass it as well to the function. Then rotate the image, call get_rectto get a new rect with the correct size and pass the centerattribute of the old rect as the centerargument. Finally, return both the rotated image and the new rect as a tuple and unpack it in the main loop.
下面的示例应该可以正常工作。新 rect 需要center旧 rect的位置,因此我们也将其传递给函数。然后旋转图像,调用get_rect以获取具有正确大小的新矩形center并将旧矩形的属性作为center参数传递。最后,将旋转后的图像和新矩形作为元组返回,并在主循环中将其解包。
import pygame as pg
def rotate(image, rect, angle):
"""Rotate the image while keeping its center."""
# Rotate the original image without modifying it.
new_image = pg.transform.rotate(image, angle)
# Get a new rect with the center of the old rect.
rect = new_image.get_rect(center=rect.center)
return new_image, rect
def main():
clock = pg.time.Clock()
screen = pg.display.set_mode((640, 480))
gray = pg.Color('gray15')
blue = pg.Color('dodgerblue2')
image = pg.Surface((320, 200), pg.SRCALPHA)
pg.draw.polygon(image, blue, ((0, 0), (320, 100), (0, 200)))
# Keep a reference to the original to preserve the image quality.
orig_image = image
rect = image.get_rect(center=(320, 240))
angle = 0
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
angle += 2
image, rect = rotate(orig_image, rect, angle)
screen.fill(gray)
screen.blit(image, rect)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
Here's another example with a rotating pygame sprite.
这是另一个带有旋转 pygame 精灵的示例。
import pygame as pg
class Entity(pg.sprite.Sprite):
def __init__(self, pos):
super().__init__()
self.image = pg.Surface((122, 70), pg.SRCALPHA)
pg.draw.polygon(self.image, pg.Color('dodgerblue1'),
((1, 0), (120, 35), (1, 70)))
# A reference to the original image to preserve the quality.
self.orig_image = self.image
self.rect = self.image.get_rect(center=pos)
self.angle = 0
def update(self):
self.angle += 2
self.rotate()
def rotate(self):
"""Rotate the image of the sprite around its center."""
# `rotozoom` usually looks nicer than `rotate`. Pygame's rotation
# functions return new images and don't modify the originals.
self.image = pg.transform.rotozoom(self.orig_image, self.angle, 1)
# Create a new rect with the center of the old rect.
self.rect = self.image.get_rect(center=self.rect.center)
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
all_sprites = pg.sprite.Group(Entity((320, 240)))
while True:
for event in pg.event.get():
if event.type == pg.QUIT:
return
all_sprites.update()
screen.fill((30, 30, 30))
all_sprites.draw(screen)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
回答by Rabbid76
Short answer:
简短的回答:
Store the center of the source image rectangle and update the center of the rotated image rectangle after the rotation, by the stored center position and return a tuple of the rotated image and the rectangle:
存储源图像矩形的中心并在旋转后更新旋转后的图像矩形的中心,通过存储的中心位置并返回旋转图像和矩形的元组:
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
Or write a function which rotates and .blitthe image:
或者编写一个旋转和.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)
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.
图像 ( pygame.Surface) 可以旋转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.
这是原因,因为旋转图像的边界矩形总是大于原始图像的边界矩形(除了某些旋转 90 度的倍数)。
由于多次复制,图像会失真。每次旋转都会产生一个小错误(不准确)。误差的总和在增加,图像在衰减。
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.
这可以通过保留原始图像并将通过单个旋转操作生成的图像从原始图像“blit”来解决。
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 boxof the image before the rotation and after the rotation.
For the following math pygame.math.Vector2is 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
这可以通过比较旋转前和旋转后图像的轴对齐边界框来补偿。
对于以下数学pygame.math.Vector2使用。注意屏幕坐标中的 y 指向屏幕下方,但数学 y 轴点从底部到顶部。这会导致在计算过程中必须“翻转”y 轴
Set up a list with the 4 corner points of the bounding box:
用边界框的 4 个角点设置一个列表:
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:
通过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:
通过将旋转框的最小值添加到位置来计算图像左上点的“补偿”原点。对于 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)
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.
在以下示例程序中,该函数blitRotate(surf, image, pos, originPos, angle)执行上述所有步骤并将旋转图像“blit”到表面。
surfis the target Surfaceimageis the Surface which has to be rotated andblitposis the position of the pivot on the target Surfacesurf(relative to the top left ofsurf)originPosis position of the pivot on theimageSurface (relative to the top left ofimage)angleis the angle of rotation in degrees
surf是目标曲面image是必须旋转的表面和blitpos是目标 Surface 上的枢轴位置surf(相对于 的左上角surf)originPos是image表面上枢轴的位置(相对于 的左上角image)angle是以度为单位的旋转角度
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()
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)


