Mih*_*iha 13 python pygame rotation
我一直试图在使用中围绕其中心旋转图像,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
Run Code Online (Sandbox Code Playgroud)
简短答案:
存储源图像矩形的中心,并在旋转后通过存储的中心位置更新旋转后图像矩形的中心,并返回旋转后图像和矩形的元组:
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
Run Code Online (Sandbox Code Playgroud)
或编写一个旋转.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)
Run Code Online (Sandbox Code Playgroud)
长答案:
对于以下示例和说明,我将使用由渲染文本生成的简单图像:
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))
Run Code Online (Sandbox Code Playgroud)
图像(pygame.Surface)可以旋转pygame.transform.rotate。
如果以循环方式逐步进行此操作,则图像会失真并迅速增加:
while not done:
# [...]
image = pygame.transform.rotate(image, 1)
screen.blit(image, pos)
pygame.display.flip()
Run Code Online (Sandbox Code Playgroud)
这是因为旋转图像的边界矩形始终大于原始图像的边界矩形(某些旋转角度为90度的倍数除外)。
由于多份复印,图像变形。每次旋转都会产生一个小的误差(不准确)。误差的总和正在增加,并且图像衰减。
可以通过保留原始图像并“变白”由单个旋转操作生成的图像来形成原始图像。
angle = 0
while not done:
# [...]
rotated_image = pygame.transform.rotate(image, angle)
angle += 1
screen.blit(rotated_image, pos)
pygame.display.flip()
Run Code Online (Sandbox Code Playgroud)
现在,图像似乎可以任意更改其位置,因为图像的大小随旋转而变化,并且原点始终位于图像边界矩形的左上方。
这可以通过比较旋转之前和旋转之后图像的轴对齐边界框来补偿。
对于以下数学运算pygame.math.Vector2。请注意,在屏幕坐标中y指向屏幕下方,但是数学y轴指向从底部到顶部。这导致在计算过程中必须“翻转” y轴
用边界框的四个角点设置一个列表:
w, h = image.get_size()
box = [pygame.math.Vector2(p) for p in [(0, 0), (w, 0), (w, -h), (0, -h)]]
Run Code Online (Sandbox Code Playgroud)
将向量旋转到拐角点pygame.math.Vector2.rotate:
box_rotate = [p.rotate(angle) for p in box]
Run Code Online (Sandbox Code Playgroud)
获取旋转点的最小值和最大值:
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])
Run Code Online (Sandbox Code Playgroud)
通过将旋转框的最小值添加到该位置来计算图像左上点的“补偿”原点。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)
Run Code Online (Sandbox Code Playgroud)
甚至可以在原始图像上定义枢轴。必须计算枢轴相对于图像左上角的“平移”,并且图像的“ blit”位置必须通过平移进行位移。
定义一个枢轴,例如在图像的中心:
origin = (pos[0] + min_box[0], pos[1] - max_box[1])
rotated_image = pygame.transform.rotate(image, angle)
screen.blit(rotated_image, origin)
Run Code Online (Sandbox Code Playgroud)
计算旋转的枢轴的平移:
pivot = pygame.math.Vector2(w/2, -h/2)
Run Code Online (Sandbox Code Playgroud)
最后计算旋转图像的原点:
pivot_rotate = pivot.rotate(angle)
pivot_move = pivot_rotate - pivot
Run Code Online (Sandbox Code Playgroud)
在下面的示例程序中,该函数blitRotate执行上述所有步骤,并将旋转后的图像“融合”到表面上。pos是图像的位置。originPos是图像上放置的点pos和枢轴:
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)
Run Code Online (Sandbox Code Playgroud)
您正在删除旋转创建的矩形。您需要保留rect,因为它在旋转时会改变大小。
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()
Run Code Online (Sandbox Code Playgroud)
top的回答有一些问题:函数中需要有前一个rect的位置,这样我们才能把它赋值给新的rect,例如:
rect = new_image.get_rect(center=rect.center)
Run Code Online (Sandbox Code Playgroud)
在另一个答案中,位置是通过从原始图像创建一个新的矩形获得的,但这意味着它将被定位在默认的 (0, 0) 坐标处。
下面的示例应该可以正常工作。新 rect 需要center旧 rect的位置,因此我们也将其传递给函数。然后旋转图像,调用get_rect以获取具有正确大小的新矩形center并将旧矩形的属性作为center参数传递。最后,将旋转后的图像和新的 rect 作为元组返回,并在主循环中将其解包。
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()
Run Code Online (Sandbox Code Playgroud)
这是另一个带有旋转 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()
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
18781 次 |
| 最近记录: |