Galaxy Defender taught you the framework. Stage 11 is where you cross the bridge โ from coder following along to game designer with your own idea. This is the lesson you'll remember in ten years.
Before we dive in โ can you remember from Lesson 10 (Game Loop)?
Events · Keys · Update · Collisions · Draw · Flip + Tick. Every frame runs them in that order, 60 times a second.
clock.tick(60) do?It makes the loop run at exactly 60 frames per second โ smooth on fast machines, not too slow on old ones. Without it the game would teleport on fast laptops and crawl on slow ones.
Every professional game developer remembers the moment they stopped copying tutorials and made the thing in their own head come alive on a screen. That's the moment "I can code" becomes "I am a creator". It's the moment your friends and family stop saying "cool, you're learning Python" and start saying "wait, YOU made that?"
You already have everything you need. The 10 stages of Galaxy Defender โ imports, screen, colours, player, bullets, enemies, score, states, helpers, the loop โ are the framework for every 2D game ever made. Maze games, racing games, platformers, top-down adventures, tower defence โ same 10 stages, different ingredients.
๐ฏ Today's mission: pick an idea, fill out a Game Design Document, build a playable prototype, share it with someone who's never seen it. That's the whole capstone in one sentence.
Stuck on what to make? Pick one of these four. Each one uses the exact same 10-stage framework as Galaxy Defender โ you'll just swap the sprites and tweak the rules.
Player: a sea turtle
Bullets: bubbles
Enemies: jellyfish drifting down
Background: dark blue with rising bubbles
Player: a knight
Bullets: arrows shot upward
Enemies: dragons flying from above
Background: stone wall + clouds
Player: a chef
Bullets: pizzas tossed up
Enemies: hungry customers walking left/right
Background: kitchen tiles
Player: a goalkeeper
Bullets: n/a โ dive to block balls
Enemies: footballs flying at the goal
Background: green grass + goal net
Pick a hero you love. Pick something they shoot, throw, jump on, or dodge. Pick what they win.
The rule of three: three things to do, three things to avoid, three ways to win.
๐ค๏ธ Want a full 10-project learning path? Grab the 10 Pygame Projects PDF โ ten progressively harder games in suggested order (Balloon Pop โ Catch the Star โ Rainbow Drawing โ Dodge โ Guess the Colour โ Maze โ Road โ Alien Shooter โ Snake โ Space Explorer). Project 1 ships with full code. Each one adds just one new idea, so by Project 10 you've practised every core 2D-game skill.
This is a polished version of Galaxy Defender โ your starting point if you want to skip straight to the "polished arcade game" tier. Four-way movement, three alien types, score, lives, win/lose screens. Press SPACE to start, arrows to move, SPACE to shoot.
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# GALAXY DEFENDER ยท SHOWCASE BUILD
# 4-way player, 3 alien types, sound, win + lose screens.
# Press SPACE to start, arrows to move, SPACE to shoot.
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
import pygame
import random
pygame.init()
try: pygame.mixer.init()
except: pass
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Galaxy Defender ยท Showcase")
clock = pygame.time.Clock()
# โโโ Colours โโโ
SPACE = (5, 5, 25)
WHITE = (255, 255, 255)
CYAN = (80, 230, 255) # bright player
YELLOW = (255, 215, 0)
RED = (240, 60, 60)
PURPLE = (200, 80, 200)
ORANGE = (255, 140, 40)
# โโโ Sound (each call is silent if the file isn't uploaded) โโโ
def load_sound(name):
try: return pygame.mixer.Sound(name)
except: return None
def play(s, vol=0.7):
if s:
s.set_volume(vol)
s.play()
snd_intro = load_sound("game_intro.mp3")
snd_start = load_sound("game_start.mp3")
snd_fire = load_sound("bullet_fire.mp3")
snd_over = load_sound("game_over.mp3")
def start_music():
try:
pygame.mixer.music.load("game_playing.mp3")
pygame.mixer.music.set_volume(0.35)
pygame.mixer.music.play(-1) # loop forever during play
except: pass
def stop_music():
try: pygame.mixer.music.stop()
except: pass
# ๐ต game_intro plays as soon as you press Run
play(snd_intro, vol=0.6)
# โโโ Sprite helpers โโโ
def draw_ship(s, x, y):
pygame.draw.polygon(s, CYAN,
[(x+30,y), (x+60,y+40), (x+40,y+30), (x+20,y+30), (x,y+40)])
pygame.draw.circle (s, WHITE, (x+30, y+18), 6)
pygame.draw.polygon(s, ORANGE, [(x+25,y+30),(x+35,y+30),(x+30,y+45)])
pygame.draw.polygon(s, YELLOW, [(x+27,y+30),(x+33,y+30),(x+30,y+40)])
def draw_laser(s, b):
pygame.draw.ellipse(s, ORANGE, (b.x-4, b.y-6, b.width+8, b.height+12))
pygame.draw.ellipse(s, YELLOW, b)
def draw_invader(s, e): # red square-foot alien
x, y = e.x, e.y
pygame.draw.rect (s, RED, (x+10, y+5, 30, 25)) # body
pygame.draw.rect (s, RED, (x, y+15, 50, 15)) # arms
pygame.draw.rect (s, RED, (x+5, y+30, 8, 10)) # leg
pygame.draw.rect (s, RED, (x+37, y+30, 8, 10))
pygame.draw.rect (s, WHITE,(x+16, y+12, 6, 6)) # eyes
pygame.draw.rect (s, WHITE,(x+28, y+12, 6, 6))
def draw_saucer(s, e): # yellow disc alien
x, y = e.x, e.y
pygame.draw.ellipse(s, YELLOW, (x, y+12, 50, 22)) # disc
pygame.draw.ellipse(s, ORANGE, (x+12, y+2, 26, 18)) # dome
pygame.draw.circle (s, RED, (x+25, y+10), 4) # red light
for i in range(5):
pygame.draw.circle(s, ORANGE, (x+5+i*10, y+22), 2) # rim lights
def draw_crab(s, e): # purple crab alien
x, y = e.x, e.y
pygame.draw.ellipse(s, PURPLE,(x+5, y+10, 40, 25)) # body
pygame.draw.circle (s, WHITE, (x+15, y+18), 4) # eyes
pygame.draw.circle (s, WHITE, (x+35, y+18), 4)
pygame.draw.circle (s, (0,0,0),(x+15, y+18), 2)
pygame.draw.circle (s, (0,0,0),(x+35, y+18), 2)
pygame.draw.line (s, PURPLE,(x+8, y+8), (x+2, y), 3) # antennae
pygame.draw.line (s, PURPLE,(x+42,y+8), (x+48,y), 3)
for dx in (5, 15, 25, 35, 45): # legs
pygame.draw.line(s, PURPLE,(x+dx,y+33),(x+dx,y+42), 2)
ALIEN_DRAWERS = [draw_invader, draw_saucer, draw_crab]
# โโโ Starfield โโโ
stars = [[random.randint(0, WIDTH), random.randint(0, HEIGHT),
random.randint(1, 2), random.choice([1, 1, 2, 3])]
for _ in range(100)]
def update_stars():
for st in stars:
st[1] += st[3]
if st[1] > HEIGHT:
st[0], st[1] = random.randint(0, WIDTH), 0
def draw_stars(s):
for sx, sy, sz, _ in stars:
pygame.draw.circle(s, WHITE, (sx, sy), sz)
# โโโ Game state โโโ
font = pygame.font.SysFont("Arial", 28)
big_font = pygame.font.SysFont("Arial", 64, bold=True)
def draw_text(text, x, y, color=WHITE, big=False, centre=False):
f = big_font if big else font
img = f.render(text, True, color)
if centre: x -= img.get_width() // 2
screen.blit(img, (x, y))
player = pygame.Rect(370, 500, 60, 40)
bullets, enemies, kinds = [], [], []
score, lives, level = 0, 3, 1
state = "MENU"
TARGET = 30
def spawn_enemy():
enemies.append(pygame.Rect(random.randint(0, WIDTH-50),
random.randint(-300, -50), 50, 40))
kinds.append(random.randint(0, 2))
def reset_game():
global score, lives, level, state, bullets, enemies, kinds
score, lives, level = 0, 3, 1
bullets, enemies, kinds = [], [], []
player.x, player.y = 370, 500
for _ in range(5): spawn_enemy()
state = "PLAYING"
for _ in range(5): spawn_enemy()
# โโโ Game loop โโโ
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT: running = False
if event.type == pygame.KEYDOWN:
if state == "MENU" and event.key == pygame.K_SPACE:
play(snd_start) # ๐ต game_start jingle
start_music() # ๐ต game_playing music loops
reset_game()
elif state == "PLAYING" and event.key == pygame.K_SPACE:
bullets.append(pygame.Rect(player.x + 27, player.y, 6, 18))
play(snd_fire, vol=0.5) # ๐ต bullet_fire on every shot
elif state in ("GAME_OVER", "WIN") and event.key == pygame.K_r:
play(snd_start)
start_music()
reset_game()
keys = pygame.key.get_pressed()
update_stars()
screen.fill(SPACE)
draw_stars(screen)
if state == "MENU":
draw_text("GALAXY DEFENDER", WIDTH//2, 180, color=YELLOW, big=True, centre=True)
draw_text("Arrow keys to move ยท SPACE to shoot", WIDTH//2, 280, centre=True)
draw_text("First to " + str(TARGET) + " wins!", WIDTH//2, 320, color=CYAN, centre=True)
draw_text("Press SPACE to start", WIDTH//2, 400, color=YELLOW, centre=True)
elif state == "PLAYING":
speed = 5 + level
if keys[pygame.K_LEFT] and player.x > 0: player.x -= speed
if keys[pygame.K_RIGHT] and player.x < WIDTH - 60: player.x += speed
if keys[pygame.K_UP] and player.y > 0: player.y -= speed
if keys[pygame.K_DOWN] and player.y < HEIGHT - 40: player.y += speed
for b in bullets[:]:
b.y -= 12
if b.y < 0: bullets.remove(b)
enemy_speed = 2 + level
for i, e in enumerate(enemies):
e.y += enemy_speed
if e.y > HEIGHT:
e.y = random.randint(-200, -50)
e.x = random.randint(0, WIDTH - 50)
lives -= 1
if lives <= 0:
state = "GAME_OVER"
stop_music() # ๐ต cut the music
play(snd_over) # ๐ต game_over sting
if player.colliderect(e):
state = "GAME_OVER"
stop_music()
play(snd_over)
for b in bullets[:]:
for i, e in enumerate(enemies):
if b.colliderect(e):
if b in bullets: bullets.remove(b)
e.y = random.randint(-200, -50)
e.x = random.randint(0, WIDTH - 50)
kinds[i] = random.randint(0, 2)
score += 1
if score == TARGET:
state = "WIN"
stop_music()
if score % 10 == 0:
level += 1; spawn_enemy()
draw_ship(screen, player.x, player.y)
for b in bullets: draw_laser(screen, b)
for i, e in enumerate(enemies): ALIEN_DRAWERS[kinds[i]](screen, e)
draw_text("Score: " + str(score) + " / " + str(TARGET), 10, 10)
draw_text("Lives: " + str(lives), WIDTH - 130, 10, color=RED)
draw_text("Level: " + str(level), WIDTH//2 - 50, 10, color=CYAN)
elif state == "WIN":
draw_text("MISSION COMPLETE!", WIDTH//2, 220, color=YELLOW, big=True, centre=True)
draw_text("You saved the galaxy.", WIDTH//2, 310, color=CYAN, centre=True)
draw_text("Press R to play again", WIDTH//2, 360, centre=True)
elif state == "GAME_OVER":
draw_text("GAME OVER", WIDTH//2, 220, color=RED, big=True, centre=True)
draw_text("Final score: " + str(score), WIDTH//2, 310, color=YELLOW, centre=True)
draw_text("Press R to try again", WIDTH//2, 360, centre=True)
pygame.display.flip()
clock.tick(60)
pygame.quit()
๐ฌ Marketing tip: open this in your own Trinket, play a 20-second run, screen-record it (Mac: Shift+Cmd+5, Windows: Win+G, or use Loom), and drop the recording on your sales page. Nothing converts like watching the actual game in action.
Professional studios call this a Game Design Document (GDD). It's a one-page promise to yourself: here's what I'm building, here's how you win, here's how you lose. Without it, you'll start drawing a turtle and end up with a half-built racing game by lunchtime.
The GDD asks you 8 questions:
๐ Print the GDD template ๐ ๏ธ Capstone Starter Pack
๐๏ธ Print it. Fill it in with a pen. Don't skip this โ every kid who skips the GDD ends up rewriting their game three times. Every kid who fills it in finishes.
Open a brand-new pygame Trinket. Delete the starter. Paste the code
below โ it's the SAME 10-stage skeleton as Galaxy Defender, with
clearly-marked TODO blocks to swap your
own content in.
# โโโ CAPSTONE STARTER ยท MY GAME โโโโโโโโโโโโโโโโโโโโโโโโ
# Replace "MY GAME" with your title. Fill in the TODOs.
import pygame
import random
# 1. SCREEN
pygame.init()
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("MY GAME")
clock = pygame.time.Clock()
# 2. COLOURS โ TODO: pick your 3
BG = (0, 0, 50) # background
HERO = (0, 230, 100) # player
FOE = (255, 0, 0) # enemy
WHITE = (255, 255, 255)
# 3. SPRITE HELPERS โ TODO: replace draw_hero / draw_foe with YOUR sprites
def draw_hero(s, x, y):
pygame.draw.rect(s, HERO, (x, y, 50, 50)) # TODO: cartoonify me
def draw_foe(s, e):
pygame.draw.rect(s, FOE, e) # TODO: cartoonify me
# 4. STATE + DATA
state = "MENU"
player = pygame.Rect(370, 500, 50, 50)
enemies = [pygame.Rect(random.randint(0, WIDTH-50),
random.randint(-300, 0), 50, 50) for _ in range(5)]
score, lives = 0, 3
font = pygame.font.SysFont("Arial", 28)
big_font = pygame.font.SysFont("Arial", 64, bold=True)
def draw_text(text, x, y, color=WHITE, big=False):
f = big_font if big else font
screen.blit(f.render(text, True, color), (x, y))
def reset_game():
global score, lives, state
score, lives, state = 0, 3, "PLAYING"
player.x = 370
for e in enemies:
e.y, e.x = random.randint(-300, -50), random.randint(0, WIDTH-50)
# 5. GAME LOOP
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT: running = False
if event.type == pygame.KEYDOWN:
if state == "MENU" and event.key == pygame.K_SPACE:
state = "PLAYING"
if state == "GAME_OVER" and event.key == pygame.K_r:
reset_game()
keys = pygame.key.get_pressed()
screen.fill(BG)
if state == "MENU":
draw_text("MY GAME", WIDTH//2 - 140, 220, color=HERO, big=True)
draw_text("Press SPACE to start", WIDTH//2 - 130, 320)
elif state == "PLAYING":
# TODO: your movement
if keys[pygame.K_LEFT] and player.x > 0: player.x -= 5
if keys[pygame.K_RIGHT] and player.x < WIDTH - 50: player.x += 5
for e in enemies:
e.y += 3
if e.y > HEIGHT:
e.y, e.x = random.randint(-200, -50), random.randint(0, WIDTH-50)
lives -= 1
if lives <= 0: state = "GAME_OVER"
if player.colliderect(e):
state = "GAME_OVER"
draw_hero(screen, player.x, player.y)
for e in enemies: draw_foe(screen, e)
draw_text("Score: " + str(score), 10, 10)
draw_text("Lives: " + str(lives), WIDTH - 130, 10)
elif state == "GAME_OVER":
draw_text("GAME OVER", WIDTH//2 - 150, 220, color=FOE, big=True)
draw_text("Press R to restart", WIDTH//2 - 110, 320)
pygame.display.flip()
clock.tick(60)
pygame.quit()
This already runs! Press SPACE, dodge red squares, press R when you die.
Your job: swap the colours, write draw_hero()
and draw_foe() sprites, change the speed, add
bullets if your game needs them.
Don't try to build it all at once. Break it down. Each "day" is one short coding session โ you could blitz the whole thing in a weekend, or spread it across two weeks.
Idea + GDD
Fill out the Game Design Document. Sketch your hero on paper. Don't write any code yet.
Setup + colours + hero on screen (still)
Paste the starter skeleton. Pick your colours. Get YOUR hero drawn on screen with arrow-key movement. That's it for today.
Enemies / collectibles
Add the things the player dodges, shoots, or collects. Use the recycle pattern from Lesson 6.
Score & lives
Wire collisions into the score and lives counters. Show them on screen.
Menu + game-over screen
Title screen with "Press SPACE to start". Game over screen with "Press R to play again". Use Lesson 8's state pattern.
Polish
Better sprites, starfield/clouds/seaweed background, sound effects (optional), animations. Use the Polish Checklist below.
Playtest + ship
Get a friend or family member to play. Watch them silently. Fix the one thing they couldn't figure out. Share the Trinket link.
These are the small touches that turn "kid project" into "real game". Tick them off one by one.
When you're ready to show the game to someone, run through these three sentences out loud. Every professional indie developer does this same pitch at game festivals.
๐ค Practise it out loud once before you show anyone. It transforms how confident you sound.
You're now through the beginner gate. You don't need NextGen Coders Lab to teach you the next thing โ you're ready to learn from the same tools real developers use.
You shipped a Python game. That puts you ahead of 99 % of kids your age. Now put it on paper โ type your name into the certificate page and save the PDF. Frame it. Pin it on your wall. Show your school. You earned it.
Galaxy Defender taught you the framework. The Rocket Ladder. The Game Loop. The Code Recipe. Every lesson stacked on the last.
But your capstone โ the game YOU designed, the sprites YOU drew, the rules YOU invented โ that one's yours forever. Years from now, when you're in a fancy job interview, building an indie hit, or teaching your own kid Python, that's the game you'll talk about. Not Galaxy Defender. The one you made.
You're not a Python student anymore. You're a game developer. Now go make something nobody's ever seen before. ๐
Print these. Fill them in. Pin them on the wall above your desk.
๐ Game Design Document (printable) ๐ ๏ธ Capstone Starter Pack ๐ค๏ธ 10 Pygame Projects (learning path) ๐จ Sprite Bonus reference ๐ Teacher & Parent Guide ๐ Completion Certificate
๐ All your Code Libraries โ every mini-game from every lesson, in one place.
๐ L1 Library ๐ L2 ๐ L3 ๐ L4 ๐ L5 ๐ L6 ๐ L7 ๐ L8 ๐ L9 ๐ L10
๐บ๏ธ The big-picture handouts
๐ Big Picture ๐ Galaxy Map ๐ Rocket Ladder ๐ ALL Infographics