Skip to main content

Stage 4: Spawn Aliens

Course progressStage 4 of 10
~35 min
One game, one Trinket

Keep building in your saved Python Arcade project.

This stage is part of the same game you started in Setup. Do the work in your own remixed Trinket — it’s the workspace on the right of this page.

Build

enemy turtles that appear at random positions

Learn

how to reuse a pattern you already know with a brand-new trigger

Ship

aliens spawning near the top of the screen

The big idea

Last stage you built the most reusable pattern in the whole course: a list of game objects + a game loop that walks the list. Today we use it again. Aliens are temporary game objects, just like lasers — they appear, move, and get cleaned up.

What changes is the trigger. Lasers spawn because a player presses a key. Aliens have no one pressing a key for them. They need to spawn on their own, at a steady rhythm, in different places, so the player can't predict where the next threat is coming from.

Stage 3 — Lasers: Stage 4 — Aliens:

player presses space ─→ random chance per frame
│ │
▼ ▼
fire_laser() spawn_alien()
│ │
▼ ▼
lasers.append(...) aliens.append(...)

The trigger is the only thing that changes. The list, the append, the cleanup — all reused.

The new tool today is randomness. Random means unpredictable on purpose. Python's random library gives us two things we need: pick a random number in a range (for the alien's X position) and "flip a weighted coin" (to decide whether to spawn this frame at all).

A tiny chance, checked many times per second, becomes a steady stream. If we spawn with a 3% chance each frame and the game runs at 30 frames per second, we get about one alien per second on average — sometimes two close together, sometimes a quiet beat. That rhythm is what makes the game feel alive.

Before you start

Your cannon should move, and pressing space should fire lasers.

Build it

Step 1 — Bring in Python's random library

The random library is part of Python — we don't install it, we just import it so this file can use it.

At the top of the file, with your other imports, add:

import random

Step 2 — Decide the alien rules

Just like the laser cap, we make the alien numbers easy to tune. ALIEN_SPAWN_CHANCE is the heartbeat. ALIEN_START_Y is the line they spawn on.

Add these near your other constants:

ALIEN_SPAWN_CHANCE = 0.03
ALIEN_START_Y = TOP - 40
aliens = []

aliens = [] is the empty list, exactly like lasers = [] from Stage 3. The shape of the pattern is already familiar.

Step 3 — Write the spawn function

This function builds one alien — a new Turtle, colored and shaped, dropped at a random X position near the top — and puts it in the list. From that moment on, the game owns the alien through the list.

Add this above the game loop:

def spawn_alien():
alien = turtle.Turtle()
alien.penup()
alien.color("lime")
alien.shape("circle")
alien.turtlesize(1.2)
x = random.randint(int(LEFT + 40), int(RIGHT - 40))
alien.setposition(x, ALIEN_START_Y)
aliens.append(alien)

The random.randint(a, b) call returns a whole number from a up through b. We wrap LEFT + 40 and RIGHT - 40 in int(...) because those values are fractions (Python divided the window width by 2), and randint only deals in whole numbers.

The 40 margins on each side are an invisible-walls move, same idea as Stage 2: we don't want aliens spawning on the screen edge where the cannon can't reach them.

Step 4 — Decide whether to spawn each frame

Now we wire the spawn into the game loop. Every frame, we flip a coin — and if the coin says "spawn," we do.

At the top of your while True: loop, add:

if random.random() < ALIEN_SPAWN_CHANCE:
spawn_alien()

random.random() (no arguments) returns a fresh number from 0 to just-under-1 every time you call it. If that number lands below 0.03, we spawn. That's our 3% chance, checked every frame.

Run the game. Aliens should drop in near the top, spaced apart, in unpredictable spots.

Understand it

The whole stage is a lesson in pattern reuse. Look at your code side-by-side: aliens is to lasers what spawn_alien is to fire_laser. The trigger changed, the appearance changed, the position rules changed — but the shape of the code is identical. Recognizing this is a real coding superpower. Next time you need an extra system in a game, ask: "is this a list of things that appear and disappear?" If yes, you already know the bones.

Probability-per-frame is worth pausing on. We could have written code that spawns an alien exactly every 30 frames — a perfect metronome. Instead we used randomness. The reason is feel: a perfect metronome teaches the player the pattern, and once they know it the game is solved. A small random chance is slightly different every game, which keeps the pressure honest. Designed unpredictability is what separates a game from a worksheet.

We deliberately set ALIEN_SPAWN_CHANCE = 0.03 and not 0.3. The difference is huge: a 3% chance per frame gives about one alien per second; a 30% chance fills the screen in two seconds. Game tuning happens in small numbers.

Try this

Learning beat

Try this

Three short experiments. Predict before you run, then test your guess.

Predict first

Change ALIEN_SPAWN_CHANCE = 0.03 to ALIEN_SPAWN_CHANCE = 0.5. Before you run, predict what will happen. Now run it. Was it what you imagined, or worse? What does that tell you about probability times framerate?

Compare

Comment out spawn_alien inside the game loop and instead bind it to a key like you did with fire_laser (screen.onkeypress(spawn_alien, "a")). Now you spawn an alien every time you press A. Which version feels more like an arcade — random rhythm or one-per-keypress? Why?

Connect

Stage 5 makes the aliens move. Look at your Stage 3 laser game-loop code — specifically the for laser in lasers[:]: block. What needs to change for aliens? What stays exactly the same?

Test your stage

  • Aliens appear without anyone pressing a key.
  • Aliens appear at different X positions, not always the same column.
  • The cannon and lasers still work.
  • The screen doesn't instantly flood with enemies.
  • Design check. Watch the game for thirty seconds without playing. Does the alien rhythm feel like the game is breathing, or like the metronome is broken?

If it breaks

  • No aliens appear. Temporarily set ALIEN_SPAWN_CHANCE = 0.2 to confirm the spawn function works at all. If aliens appear at 0.2 but not at 0.03, the chance is just low — be patient or pick a slightly higher value.
  • Python says random is not defined. The import random line is missing or sitting below code that uses it. Imports go at the top of the file.
  • Aliens appear outside the screen. Look at random.randint(int(LEFT + 40), int(RIGHT - 40)). The margins (+ 40 and - 40) keep them inside. If you removed the int(...) wrappers, Python will complain that randint needs whole numbers.
  • Aliens spawn but the rest of the game freezes. Check that spawn_alien() is not in an infinite loop by mistake (like inside its own while True:). It should be one line: build alien, append, done.