簡體   English   中英

如何使用 Pygame 圍繞其中心旋轉圖像?

[英]How do I rotate an image around its center using 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, 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

或者編寫一個旋轉和.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)

長答案:

圖像( 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()

現在圖像似乎可以隨意改變它的位置,因為圖像的大小會隨着旋轉而改變,並且原點始終是圖像邊界矩形的左上角。

這可以通過比較旋轉前后圖像的軸對齊邊界框來補償。
對於以下數學,使用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)

甚至可以在原始圖像上定義一個樞軸。 計算從圖像中心到樞軸的偏移向量並旋轉向量。 向量可以用pygame.math.Vector2表示,也可以用pygame.math.Vector2.rotate旋轉。 請注意, pygame.math.Vector2.rotate的旋轉方向與pygame.transform.rotate的相反。 因此必須反轉角度:

計算從圖像中心到樞軸的向量:

image_rect = image.get_rect(topleft = (pos[0] - originPos[0], pos[1]-originPos[1]))
offset_center_to_pivot = pygame.math.Vector2(pos) - image_rect.center

旋轉矢量

rotated_offset = offset_center_to_pivot.rotate(-angle)

計算旋轉圖像的中心:

rotated_image_center = (pos[0] - rotated_offset.x, pos[1] - rotated_offset.y)

旋轉和blit圖像:

rotated_image = pygame.transform.rotate(image, angle)
rotated_image_rect = rotated_image.get_rect(center = rotated_image_center)

screen.blit(rotated_image, rotated_image_rect)

在下面的示例程序中,函數blitRotate(surf, image, pos, originPos, angle)執行上述所有步驟並將旋轉的圖像“blit”到表面。

  • surf是目標表面

  • image是必須旋轉和blit的 Surface

  • pos是目標 Surface surf上樞軸的位置(相對於surf的左上角)

  • originPosimage Surface 上樞軸的位置(相對於image的左上角)

  • angle是以度為單位的旋轉角度

這意味着, blitRotate的第二個參數( pos )是窗口中樞軸點的位置,第三個參數( originPos )是旋轉Surface上的樞軸點位置:


最小的例子: repl.it/@Rabbid76/PyGame-RotateAroundPivot

import pygame

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

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

    # offset from pivot to center
    image_rect = image.get_rect(topleft = (pos[0] - originPos[0], pos[1]-originPos[1]))
    offset_center_to_pivot = pygame.math.Vector2(pos) - image_rect.center
    
    # roatated offset from pivot to center
    rotated_offset = offset_center_to_pivot.rotate(-angle)

    # roatetd image center
    rotated_image_center = (pos[0] - rotated_offset.x, pos[1] - rotated_offset.y)

    # get a rotated image
    rotated_image = pygame.transform.rotate(image, angle)
    rotated_image_rect = rotated_image.get_rect(center = rotated_image_center)

    # rotate and blit the image
    surf.blit(rotated_image, rotated_image_rect)
  
    # draw rectangle around the image
    pygame.draw.rect(surf, (255, 0, 0), (*rotated_image_rect.topleft, *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('AirPlaneFront.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()

angle = 0
done = False
while not done:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = 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)
    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()

另請參閱旋轉曲面和問題的答案:

您正在刪除旋轉創建的矩形。 您需要保留矩形,因為它在旋轉時會改變大小。

如果要保留對象位置,請執行以下操作:

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()

置頂答案有一些問題: 函數中需要有前一個rect的位置,這樣我們才能將其分配給新的rect,例如:

rect = new_image.get_rect(center=rect.center) 

在另一個答案中,位置是通過從原始圖像創建一個新的矩形來獲得的,但這意味着它將定位在默認的 (0, 0) 坐標處。

下面的示例應該可以正常工作。 新矩形需要舊矩形的center位置,所以我們也將它傳遞給函數。 然后旋轉圖像,調用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()

這是另一個旋轉 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()

發現問題:示例效果很好,但寬度和高度需要相等的尺寸。 固定的圖片,它的工作原理。

pygame中繪制圖像所需的一切

game_display = pygame.display.set_mode((800, 600))

x = 0
y = 0
angle = 0

img = pygame.image.load("resources/image.png")
img = pygame.transform.scale(img, (50, 50)) # image size

def draw_img(self, image, x, y, angle):
    rotated_image = pygame.transform.rotate(image, angle) 
    game_display.blit(rotated_image, rotated_image.get_rect(center=image.get_rect(topleft=(x, y)).center).topleft)

# run this method with your loop
def tick():
    draw_img(img, x, y, angle)

我必須如下修改 skrx 解決方案,這種方式對我有用。

angle=0
roll = true
while roll:
    # clean surface with your background color
    gameDisplay.fill(color)
    self.image = yourImage
    rotate_image = pygame.transform.rotate(self.image, angle)
    rect = rotate_image.get_rect()
    pos = (((your_surface_width - rect.width)/2),((your_surface_height - rect.height)/2))
    gameDisplay.blit(rotate_image,pos)
    pygame.display.flip()
    angle+=2
    if angle == 360:
        roll=False 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM