简体   繁体   中英

Moving Sprites with Pygame in Python

I'm making an Asteroids clone and I have a ship that moves fluidly with the arrow keys. The ship has an xv and yv velocity, and to move it I simply change the sprite's rect.x and rect.y by xv and yv , respectively. Once I let go of the arrow keys though, I would like it to decelerate smoothly. To do this, I am currently multiplying xv and yv by .96 every frame, which both decelerates the ship and enforces a maximum velocity.

At first glance, this seems to work fine, but when you look closely the ship will have strange movement patterns; for example, sometimes it will slow down a bit before it stops moving down, but it will continue at a constant speed in the x direction. Sometimes, the sprite will stop or slow down significantly on the edges of the screen while it is in this behavior, which shouldn't happen because I have code in place so that it should loop around to the other side.

I suspect these strange behaviors have to do with rounding within the rect or sprite parts of Pygame, but I am not sure how to fix this. I have tried automatically setting the velocity to zero once it gets low enough, but the problem persists and sometimes worsens.

Here is my code for the Player class:

import pygame as pg
import Settings as s
import math
class Player(pg.sprite.Sprite):
    global rect, r, xv, yv, v, track, images, pthrust

    def __init__(self, x = (s.w-30)/2, y = (s.h-45)/2, r = math.pi, xv = 0, yv = 0, v = .5):
        pg.sprite.DirtySprite.__init__(self)
        self.r = r
        self.v = v
        self.xv = xv
        self.yv = yv
        self.rect = pg.Rect(x, y, 30, 45)
        self.radius = 1
        self.images = [pg.image.load('Player.png').convert_alpha(), pg.image.load('PThrust.png').convert_alpha()]
        self.imagec = self.images[0]
        self.image = self.imagec
        self.pthrust = False

    def thrust(self): # Called every frame while up arrow is down
        self.xv += self.v*math.sin(self.r) # Accelerate the player
        self.yv += self.v*math.cos(self.r)
        self.imagec = self.images[1]
        self.pthrust = True

    def unthrust(self): # Called when up arrow is released
        self.imagec = self.images[0]
        self.pthrust = False

    def turnLeft(self): # Called every frame while left arrow is down
        self.r += math.pi/48

    def turnRight(self): # Called every frame while right arrow is down
        self.r -= math.pi/48

    def move(self): # Called every frame
        self.xv *= .96 # Decelerates the Player
        self.yv *= .96
        self.rect.left += self.xv # Changes the rect position
        self.rect.top += self.yv
        if self.rect.centerx < 0: self.rect.centerx = s.w+self.rect.centerx # Loop around edges of screen
        if self.rect.centery < 0: self.rect.centery = s.h+self.rect.centery
        if self.rect.centerx > s.w: self.rect.centerx = self.rect.centerx-s.w
        if self.rect.centery > s.h: self.rect.centery = self.rect.centery-s.h
        self.image = pg.transform.rotate(self.imagec, math.degrees(self.r)-180) # Rotate image
        self.rect = self.image.get_rect(center=self.rect.center)

    def reset(self): # Return to center if player dies
        self.rect.x = (s.w-30)/2
        self.rect.y = (s.h-45)/2
        self.r = math.pi
        self.xv = 0
        self.yv = 0

If anyone has any ideas, they'd be greatly appreciated. Thanks in advance!

Edit: If the code for the main class would be helpful I can provide it, but I didn't think it was relevant to the issue and would take up a lot of space.

I don't observe this behavior when i change your code to use model view controller . eg instead of storing the x and y in Rect. player should store its own x and y and construct the Rect using that x and y.

I don't think pygame objects (such as Rect) should store your model directly. in general code seems to work more correct when you separate the Model from the View.

I would also recommend sepperating frame rate from game speed. eg instead of using 0.5 distance / frame use 0.5 meters / second .

I suspect these strange behaviors have to do with rounding within the rect [...]

You have to do all the computations with floating point coordinates. Add attributes self.x respectively self.y which track the position of the object. This attributes have to be used to compute the current position of the object. Round the position and synchronize it with the location of the .rect attribute, after a new position is computed. eg:

class Player(pg.sprite.Sprite):
    # [...]

    def __init__(self, x = (s.w-30)/2, y = (s.h-45)/2, r = math.pi, xv = 0, yv = 0, v = .5):
        pg.sprite.DirtySprite.__init__(self)
        # [...]
        self.x = x
        self.y = y
        self.rect = pg.Rect(self.x, self.y, 30, 45)
        # [...]

   # [...]

    def move(self): # Called every frame
        self.xv *= .96 # Decelerates the Player
        self.yv *= .96

        # Changes the rect position
        self.x += self.xv
        self.y += self.yv
        self.rect.topleft = round(self.x), round(self.y)

        # bounds check
        x_changed, y_changed = False, False
        if self.rect.centerx < 0:
            self.rect.centerx = s.w+self.rect.centerx
            x_changed = True
        if self.rect.centery < 0:
            self.rect.centery = s.h+self.rect.centery
            y_changed = True
        if self.rect.centerx > s.w:
            self.rect.centerx = self.rect.centerx-s.w
            x_changed = True
        if self.rect.centery > s.h:
            self.rect.centery = self.rect.centery-s.h
            y_changed = True

        # update self.x, self.y after collision with bounds
        if x_changed:
            self.x = self.rect.x
        if y_changed:
            self.y = self.rect.y

        # [...]

    def reset(self): # Return to center if player dies
        self.x = (s.w-30)/2
        self.y = (s.h-45)/2
        self.rect.topleft = round(self.x), round(self.y)
        # [...]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM