Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
196 views
in Technique[技术] by (71.8m points)

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

I am working on a simple game. I created a pygame sprite which and tested it by making it move forward and rotating at a consistent speed. However, it seams to be moving left and up (where sin and cos are negative) quicker than right and down (where sin and cos are positive). I tested it without moving and just rotating and it works.

Here is the code:

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)

To describe what it looks like when I run it, it is a ship on a black background that rotates counter-clockwise while moving forward in what should be a circle. But instead, it is creating a sort of spiral, slowly getting closer to the top left corner of the screen.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This is an accuracy issue. Since pygame.Rect is supposed to represent an area on the screen, a pygame.Rect object can only store integral data.

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

The fraction part of the coordinates gets lost when the new position of the player is assigned to the Rect object:

self.rect.center = (nx, ny)

Since this is done every frame, the position error will accumulate over time.

If you want to store object positions with floating point accuracy, you have to store the location of the object in separate variables respectively attributes (self.pos) and to synchronize the pygame.Rect object. round the coordinates and assign it to the location (e.g. .center) of the rectangle:

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)

See also Move and rotate.


Minimal example:

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...