Game Dev Lab — Day 1
Pygame Basics: Moving Ship
Pygame Basics + Moving Ship
# INSTRUCTOR — Game Dev Lab Day 1: Pygame Foundations
# -------------------------------------------------------
# TEACH: This student already knows Python well. Skip syntax review.
# The goal today is to get them into pygame fast and have something moving.
#
# KEY DIFFERENCES from turtle:
# - pygame uses a pixel coordinate system: (0,0) is TOP-LEFT, y increases downward
# - No global event loop — YOU write the while loop and call pygame.event.get() yourself
# - Drawing is done each frame: clear screen, draw everything, flip (show)
# - Keyboard uses pygame.key.get_pressed() for smooth held-key movement
#
# MILESTONE (show me when done):
# Ship moves left/right with arrow keys — smooth, no jitter
# Space fires a bullet that flies upward off screen
#
# EXTENSION challenges (when milestone is done):
# - Wrap ship so going off right edge appears on left
# - Limit max 3 bullets on screen at once
# - Add a speed variable and let Up/Down arrow change it
import pygame
import sys
# ── Setup ───────────────────────────────────────────────────────────────────
pygame.init()
WIDTH, HEIGHT = 600, 700
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Space Shooter — Day 1")
clock = pygame.time.Clock() # TEACH: clock.tick(FPS) keeps the game at a fixed speed
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
CYAN = (0, 220, 220)
YELLOW = (255, 255, 0)
# ── Ship ────────────────────────────────────────────────────────────────────
# TEACH: We store game objects as dicts for now. Day 3 converts these to classes.
ship = {
'x': WIDTH // 2,
'y': HEIGHT - 60,
'width': 40,
'height': 30,
'speed': 5,
}
# ── Bullets ─────────────────────────────────────────────────────────────────
bullets = [] # each bullet: {'x': ..., 'y': ..., 'speed': ...}
def fire_bullet():
bullets.append({'x': ship['x'], 'y': ship['y'] - ship['height'], 'speed': 8})
# ── Draw helpers ─────────────────────────────────────────────────────────────
def draw_ship(s):
# Triangle pointing up: three points
cx = s['x']
top = (cx, s['y'] - s['height'])
left = (cx - s['width'] // 2, s['y'])
right = (cx + s['width'] // 2, s['y'])
pygame.draw.polygon(screen, CYAN, [top, left, right])
def draw_bullet(b):
pygame.draw.ellipse(screen, YELLOW,
(b['x'] - 4, b['y'] - 10, 8, 18))
# ── Game loop ────────────────────────────────────────────────────────────────
# TEACH: every frame does exactly four things in order:
# 1. Handle events (quit, key presses)
# 2. Update state (move ship, move bullets)
# 3. Draw everything
# 4. Flip (show the new frame)
while True:
# 1. Events
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
fire_bullet()
# 2. Update
keys = pygame.key.get_pressed() # TEACH: held keys — much smoother than KEYDOWN
if keys[pygame.K_LEFT] and ship['x'] > ship['width'] // 2:
ship['x'] -= ship['speed']
if keys[pygame.K_RIGHT] and ship['x'] < WIDTH - ship['width'] // 2:
ship['x'] += ship['speed']
# Move bullets upward; remove when off screen
for b in bullets[:]: # TEACH: iterate a copy when removing during loop
b['y'] -= b['speed']
if b['y'] < 0:
bullets.remove(b)
# 3. Draw
screen.fill(BLACK)
draw_ship(ship)
for b in bullets:
draw_bullet(b)
# 4. Flip
pygame.display.flip()
clock.tick(60) # TEACH: 60 frames per second cap