使用 sprites 在 pygame 中旋转图像

Rotate an image in pygame using sprites

我正在 pygame 中制作游戏,我想旋转图像。 pygame.transform.rotate 不断增加图像尺寸,所以我想也许精灵可以帮助我旋转图像。但问题更复杂,我想点击表面的某个地方,我想让图像转向那个方向,所以物体可以进行无限次旋转。有人可以给我解释一下如何做到这一点吗?

图像尺寸变化

pygame 中的曲面无法旋转;它们都有水平宽度和垂直高度。当您加载图像时,pygame 会创建一个 Surface,其水平宽度和垂直高度等于您的图像。当您将图像旋转 45 度时 pygame 必须创建一个适合原始图像的新表面。新 Surface 的水平宽度和垂直高度必须是图像假设才能适合图像。

这是应该的。如果您遇到的问题与碰撞检测有关,我建议您尝试其他形式的碰撞检测,例如圆形,或者继续使用矩形但最小化它的大小。

向特定方向旋转

您可能应该使用矢量使图像转向您单击的位置。我通常会创建自己的向量 class,但是 pygame has its own Vector classes which should be used unless you want to create your own for learning purposes. If you don't know how add, subtract, scalar multiply, normalize and calculate angle between 向量你可能需要阅读它,否则它可能会有点复杂。无论如何,这是基本向量的一小部分 class:

import math

class Vector2D(object):

    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __add__(self, other):
        return Vector2D(self.x + other.x, self.y + other.y)

    def __sub__(self, other):
        return Vector2D(self.x - other.x, self.y - other.y)

    def __mul__(self, other):
        if isinstance(other, Vector2D):
            # Vector multiplication
            return self.x * other.x + self.y * other.y
        else:
            # Scalar multiplication
            return Vector2D(self.x * other, self.y * other)

    __radd__ = __add__
    __rsub__ = __sub__
    __rmul__ = __mul__

    def length(self):
        return (self.x ** 2 + self.y ** 2) ** (1/2)

    def angle_to(self, other, radians=False):
        """Will return the angle between this vector and the other vector."""
        if self.length() == 0 or other.length() == 0:
            return 0
        if not radians:
            return (360 / (2 * math.pi)) * (math.atan2(other.y, other.x) - math.atan2(self.y, self.x))
        else:
            return math.atan2(other.y, other.x) - math.atan2(self.y, self.x)

    def normalize(self):
        if self.length() == 0:
            return Vector2D(0, 0)
        return Vector2D(self.x / self.length(), self.y / self.length())

然后我会为您的图像创建一个 class,属性为 positionoriginal image。当您旋转图像时,pygame 会创建一个旋转的新图像。通过这样做,您的图像会丢失一些信息,因此会丢失一些质量。这就是为什么您始终应该旋转原始图像而不是旋转后的副本。

class Player(pygame.sprite.Sprite):

    def __init__(self, image_path, pos=(0, 0)):
        super(Player, self).__init__()
        self.original_image = pygame.image.load(image_path).convert()  # REMEMBER TO CONVERT!
        self.image = self.original_image  # This will reference our rotated copy.
        self.rect  = self.image.get_rect()
        self.position = Vector2D(*pos)


    def update(self):
        """Updates the player's orientation."""

        # Create a vector pointing at the mouse position.
        mouse_position = Vector2D(*pygame.mouse.get_pos())

        # Create a vector pointing from the image towards the mouse position.
        relative_mouse_position = mouse_position - self.position  

        # Calculate the angle between the y_axis and the vector pointing from the image towards the mouse position.
        y_axis = Vector2D(0, -1)
        angle = -y_axis.get_angle(relative_mouse_position)  # Negating because pygame rotates counter-clockwise.

        # Create the rotated copy.
        self.image = pygame.transform.rotate(self.original, angle).convert()  # Angle is absolute value!

        # Make sure your rect represent the actual Surface.
        self.rect = self.image.get_rect()

        # Since the dimension probably changed you should move its center back to where it was.
        self.rect.center = self.position.x, self.position.y

一个简短的例子

import pygame
pygame.init()

BACKGROUND_COLOR = (0, 0, 0)


class Player(pygame.sprite.Sprite):

    def __init__(self, position=(0, 0)):
        super(Player, self).__init__()
        self.original_image = pygame.Surface((32, 32))
        pygame.draw.lines(self.original_image, (255, 255, 255), True, [(16, 0), (0, 31), (31, 31)])
        self.image = self.original_image  # This will reference our rotated copy.
        self.rect  = self.image.get_rect()
        self.position = pygame.math.Vector2(*position)

    def update(self):
        """Updates the players orientation."""
        # Create a vector pointing at the mouse position.
        mouse_position = pygame.math.Vector2(*pygame.mouse.get_pos())

        # Create a vector pointing from the image towards the mouse position.
        relative_mouse_position = mouse_position - self.position

        # Calculate the angle between the y_axis and the vector pointing from the image towards the mouse position.
        y_axis = pygame.math.Vector2(0, -1)
        angle  = -y_axis.angle_to(relative_mouse_position )  # Subtracting because pygame rotates counter-clockwise.

        # Create the rotated copy.
        self.image = pygame.transform.rotate(self.original_image, angle).convert()  # Angle is absolute value!

        # Make sure your rect represent the actual Surface.
        self.rect = self.image.get_rect()

        # Since the dimension probably changed you should move its center back to where it was.
        self.rect.center = self.position.x, self.position.y


screen = pygame.display.set_mode((720, 480))
player = Player(position=(300, 250))

while True:
    # Handle events
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            quit()

    # Update
    player.update()

    # Render
    screen.fill(BACKGROUND_COLOR)
    screen.blit(player.image, player.rect)
    pygame.display.update()