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.