I'm new to Python, Pygame, and programming in general. I'm trying to create an Asteroids clone, but I can't figure out how to make my player sprite move and rotate at the same time. I used the Vector2D class from this answer (tried to modify it for the keyboard instead of the mouse) so my sprite is kind of rotating (it won't rotate in a complete circle), but now it won't move. I'm trying to use the up and down keys to accelerate/decelerate and the left and right arrows to turn. I want the ship to face the direction in which it is moving.
Here's the code thus far:
# an asteroids clone
try:
import sys
import random
import math
import os
import getopt
import pygame
from socket import *
from pygame.locals import *
from pygame.mixer import Sound
except ImportError, err:
print "couldn't load module. %s" % (err)
sys.exit(2)
# these are warnings if font or sound modules are not available.
if not pygame.font: print 'Warning, fonts disabled'
if not pygame.mixer: print 'Warning, sound disabled'
# VECTOR CLASS
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 get_length(self):
return (self.x ** 2 + self.y ** 2) ** (1/2)
def get_angle(self, other, radians=False):
"""Will return the angle between this vector and the other vector."""
if self.get_length() == 0 or other.get_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.get_length() == 0:
return Vector2D(0, 0)
return Vector2D(self.x / self.get_length(), self.y / self.get_length())
class Player(pygame.sprite.Sprite):
"""moves ship on screen"""
def __init__(self, image_file, pos=(0, 0)):
super(Player, self).__init__() # call Sprite initializer
self.original_image = pygame.image.load(image_file).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)
self.moving = 0 # won't move at start of game
screen = pygame.display.get_surface()
self.area = screen.get_rect()
self.speed = 10
self.state = "still"
degree = 0
self.reinit()
def reinit(self):
self.state = "still"
self.movepos = [0,0]
def update(self):
newpos = self.rect.move(self.movepos)
if self.area.contains(newpos):
self.rect = newpos
# Create a vector pointing at the key position
key_pos = self.rect.move(self.movepos)
# Create a vector pointing from the image towards the key direction
rel_key_pos = key_pos - 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(rel_key_pos) # Negating bc 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
pygame.event.pump()
def accelerate(self):
self.speed += 1
self.state = "accelerate"
def decelerate(self):
self.speed -= 1
self.state = "decelerate"
def moveleft(self):
self.movepos[0]-=(self.speed)
self.state="moveleft"
def moveright(self):
self.movepos[0]+=(self.speed)
self.state="moveright"
class Bullet():
pass
class Asteroid():
pass
class Background(pygame.sprite.Sprite):
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) #call Sprite initializer
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
# main event loop
def main():
# initialize screen
pygame.init()
pygame.mixer.init()
pygame.key.set_repeat(500,30)
screen = pygame.display.set_mode((1280, 1024))
pygame.display.set_caption('Asteroids')
pygame.mouse.set_visible(0)
# make background
background = Background('data\stars1.jpg', [0,0])
# prepare background music
pygame.mixer.music.load('data\patakasmusic.wav')
pygame.mixer.music.play(-1)
# load player sprite
global player
player = Player('data\ship.png', [0,0])
# initialize player sprite
playersprite = pygame.sprite.RenderPlain((player))
# initialize clock
clock = pygame.time.Clock()
# event loop
while 1:
clock.tick(60)
# event loop
for event in pygame.event.get():
if event.type == QUIT:
return
elif event.type == KEYDOWN:
if event.key == K_UP:
player.accelerate()
if event.key == K_DOWN:
player.decelerate()
if event.key == K_LEFT:
player.moveleft()
if event.key == K_RIGHT:
player.moveright()
elif event.type == KEYUP:
if event.key == K_UP or event.key == K_DOWN:
player.movepos = [0,0]
screen.fill([255, 255, 255])
screen.blit(background.image, background.rect)
screen.blit(player.image, (500,500))
playersprite.draw(screen)
playersprite.update()
pygame.display.update()
playersprite.update()
pygame.display.flip()
if __name__ == '__main__': main()
I'd use another vector to store the direction
of the sprite and also add an angle_speed
attribute. When the user wants to turn the ship, set the angle_speed
to the desired value in degrees and in the update
method rotate the direction vector by the angle speed. To move the sprite, multiply the direction
by self.speed
to get the velocity and add it to the self.position
, then update the rect.
I recommend to use the pygame.math.Vector2
class, since it has more features and is also pretty fast because it's implemented in C.
import math
import pygame as pg
from pygame.math import Vector2
class Player(pg.sprite.Sprite):
def __init__(self, pos=(420, 420)):
super(Player, self).__init__()
self.image = pg.Surface((70, 50), pg.SRCALPHA)
pg.draw.polygon(self.image, (50, 120, 180), ((0, 0), (0, 50), (70, 25)))
self.original_image = self.image
self.rect = self.image.get_rect(center=pos)
self.position = Vector2(pos)
self.direction = Vector2(1, 0) # A unit vector pointing rightward.
self.speed = 2
self.angle_speed = 0
self.angle = 0
def update(self):
if self.angle_speed != 0:
# Rotate the direction vector and then the image.
self.direction.rotate_ip(self.angle_speed)
self.angle += self.angle_speed
self.image = pg.transform.rotate(self.original_image, -self.angle)
self.rect = self.image.get_rect(center=self.rect.center)
# Update the position vector and the rect.
self.position += self.direction * self.speed
self.rect.center = self.position
def main():
pg.init()
screen = pg.display.set_mode((1280, 720))
player = Player((420, 420))
playersprite = pg.sprite.RenderPlain((player))
clock = pg.time.Clock()
done = False
while not done:
clock.tick(60)
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.KEYDOWN:
if event.key == pg.K_UP:
player.speed += 1
elif event.key == pg.K_DOWN:
player.speed -= 1
elif event.key == pg.K_LEFT:
player.angle_speed = -4
elif event.key == pg.K_RIGHT:
player.angle_speed = 4
elif event.type == pg.KEYUP:
if event.key == pg.K_LEFT:
player.angle_speed = 0
elif event.key == pg.K_RIGHT:
player.angle_speed = 0
playersprite.update()
screen.fill((30, 30, 30))
playersprite.draw(screen)
pg.display.flip()
if __name__ == '__main__':
main()
pg.quit()
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.