在 pygame 中旋转时,船舶向上和向左移动的速度比向下和向右移动的速度快

Ship moves up and left faster than down and right when rotating in pygame

我正在开发一个简单的游戏。我创建了一个 pygame 精灵,并通过使其以一致的速度向前移动和旋转来对其进行测试。然而,它向左和向上移动(其中 sin 和 cos 为负)比向右和向下移动(其中 sin 和 cos 为正)更快。我在没有移动和旋转的情况下测试了它,它可以工作。

代码如下:

import pygame
import sys
from math import cos, sin, pi
from time import sleep

SCREEN_WIDTH = 800
SCREEN_HEIGHT = 800

FPS = 60

pygame.init()
DISPLAY = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
CLOCK = pygame.time.Clock()

RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)

SHIP_BLUE_IMG = pygame.image.load('./spaceshooter/PNG/playerShip1_blue.png').convert()
SHIP_RED_IMG = pygame.image.load('./spaceshooter/PNG/playerShip1_red.png').convert()
LASER_BLUE_IMG = pygame.image.load('./spaceshooter/PNG/Lasers/laserBlue16.png').convert()
LASER_RED_IMG = pygame.image.load('./spaceshooter/PNG/Lasers/laserRed16.png').convert()

LASER_SPEED = 10
PLAYER_SHIP_SPEED = 5

all_sprites = pygame.sprite.Group()


class Player(pygame.sprite.Sprite):
    def __init__(self, img, pos, angle):
        super().__init__()
        img.set_colorkey((0, 0, 0, 0))
        self.angle = angle
        self.original_img = pygame.transform.rotate(img, 180) # Becase these images come upside down
        self.image = self.original_img
        self.rect = self.image.get_rect()
        self.rect.center = pos

        self._update_image() 

    def _update_image(self):
        x, y = self.rect.center
        self.image = pygame.transform.rotate(self.original_img, self.angle)
        self.rect = self.image.get_rect()
        self.rect.center = (x, y)

    def _get_radeons(self):
        return (self.angle*pi)/180

    def rotate(self, degrees):
        self.angle += degrees
        self._update_image()

    def update(self):
        self.rotate(5)
        x, y = self.rect.center
        nx = sin(self._get_radeons())*PLAYER_SHIP_SPEED + x
        ny = cos(self._get_radeons())*PLAYER_SHIP_SPEED + y
        self.rect.center = (nx, ny)


player = Player(SHIP_BLUE_IMG, (300, 300), 45)
all_sprites.add(player)

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

    DISPLAY.fill(BLACK)
    all_sprites.update()
    all_sprites.draw(DISPLAY)
    pygame.display.update()
    CLOCK.tick(FPS)

描述一下我运行它的样子,它是一艘黑色背景的船,一边逆时针旋转,一边向前移动,应该是一个圆圈。但相反,它正在形成一种螺旋,慢慢靠近屏幕的左上角。

这是一个准确性问题。由于 pygame.Rect 应该表示屏幕上的一个区域,因此 pygame.Rect 对象只能存储整数数据。

The coordinates for Rect objects are all integers. [...]

当玩家的新位置分配给 Rect 对象时,坐标的小数部分丢失:

self.rect.center = (nx, ny)

由于每帧都这样做,位置误差会随着时间累积。

如果要以浮点精度存储对象位置,则必须将对象的位置分别存储在单独的变量属性 (self.pos) 中,并同步 pygame.Rect 对象。 round坐标并将其分配给矩形的位置(例如.center):

class Player(pygame.sprite.Sprite):
    def __init__(self, img, pos, angle):
        # [...]

        self.pos = pos

    # [...]

    def update(self):
        self.rotate(5)
        nx = sin(self._get_radeons())*PLAYER_SHIP_SPEED + self.pos[0]
        ny = cos(self._get_radeons())*PLAYER_SHIP_SPEED + self.pos[1]
        self.pos = (nx, ny)
        self.rect.center = round(nx), round(ny)

另见 Move and rotate


最小示例:

import math
import pygame

class Player(pygame.sprite.Sprite):
    def __init__(self, img, pos, angle):
        super().__init__()
        img.set_colorkey((0, 0, 0, 0))
        self.angle = angle
        self.original_img = pygame.transform.rotate(img, 180)
        self.image = self.original_img
        self.rect = self.image.get_rect()
        self.rect.center = pos
        self.pos = pos
        self.speed = 5
        self.angle_step = 5
        self._update_image() 

    def _update_image(self):
        x, y = self.rect.center
        self.image = pygame.transform.rotate(self.original_img, self.angle)
        self.rect = self.image.get_rect()
        self.rect.center = (x, y)

    def update(self):
        self.angle += self.angle_step
        self._update_image()
        nx = math.sin(math.radians(self.angle)) * self.speed + self.pos[0]
        ny = math.cos(math.radians(self.angle)) * self.speed + self.pos[1]
        self.pos = (nx, ny)
        self.rect.center = round(nx), round(ny)

pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
background = pygame.Surface(window.get_size())
background.set_alpha(5)

all_sprites = pygame.sprite.Group()
ship_image = pygame.image.load('Rocket64.png').convert_alpha()
player = Player(ship_image, (window.get_width()//2-40, window.get_height()//2+40), 45)
all_sprites.add(player)

run = True
while run:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    all_sprites.update()

    window.blit(background, (0, 0))
    all_sprites.draw(window)
    pygame.display.update()

pygame.quit()
exit()