The Player Class
With the loop running, it's time to put something on screen: the player's ship. This is also where object-oriented programming starts paying off, every asteroid and bullet you add later will follow the same pattern as this Player class.
Defining the class
import math
import pygame
class Player:
def __init__(self, x, y):
self.x = x
self.y = y
self.angle = 0 # degrees, 0 = facing up
self.speed = 0
self.radius = 15
def rotate(self, direction):
self.angle += direction * 4 # degrees per frame
def thrust(self):
self.speed = min(self.speed + 0.15, 6) # accelerate, capped at a max speed
def update(self):
radians = math.radians(self.angle)
self.x += -math.sin(radians) * self.speed
self.y += math.cos(radians) * self.speed
self.speed *= 0.99 # gentle drag, so the ship coasts to a stop instead of forever
def draw(self, screen):
radians = math.radians(self.angle)
tip = (self.x + math.sin(radians) * self.radius, self.y - math.cos(radians) * self.radius)
left = (self.x - math.cos(radians) * self.radius * 0.6, self.y - math.sin(radians) * self.radius * 0.6)
right = (self.x + math.cos(radians) * self.radius * 0.6, self.y + math.sin(radians) * self.radius * 0.6)
pygame.draw.polygon(screen, (255, 255, 255), [tip, left, right])
__init__stores the ship's position (x,y), facingangle, currentspeed, and aradiusyou'll reuse for collision checks later.rotateandthrustdon't move the ship directly, they just change its angle/speed,updateis what actually applies that to the position every frame, using basic trigonometry to convert an angle into an x/y direction.drawcomputes three points (nose and two rear corners) from the ship's angle and position, then hands them topygame.draw.polygonto render a simple triangle ship, classic Asteroids style.
Wiring it into the loop
player = Player(400, 300)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
player.rotate(-1)
if keys[pygame.K_RIGHT]:
player.rotate(1)
if keys[pygame.K_UP]:
player.thrust()
player.update()
screen.fill((10, 10, 30))
player.draw(screen)
pygame.display.flip()
clock.tick(60)
pygame.key.get_pressed() returns the state of every key as a list-like object, checking keys[pygame.K_LEFT] each frame (rather than only reacting to individual key-down events) is what gives you smooth, held-down rotation and thrust instead of one press = one tiny nudge.
TIP
Notice rotate/thrust only change angle/speed, while update is the only method that touches x/y. Separating "what changed" from "apply the change" like this makes it much easier to add things like screen-wrap or drag later without touching your input-handling code.