Build Asteroids using Python and Pygame

Lesson 3 of 7

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), facing angle, current speed, and a radius you'll reuse for collision checks later.
  • rotate and thrust don't move the ship directly, they just change its angle/speed, update is what actually applies that to the position every frame, using basic trigonometry to convert an angle into an x/y direction.
  • draw computes three points (nose and two rear corners) from the ship's angle and position, then hands them to pygame.draw.polygon to 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.

📝 Player Class Quiz

Passing score: 70%
  1. 1.Which function lets you check every frame whether a key is currently held down?

  2. 2.In the Player class, the rotate() and thrust() methods directly change the ship's x and y position.

  3. 3.pygame.draw.____(screen, color, points) draws a shape from a list of (x, y) points, used here for the ship.