Game Design II — Make & Ship
Organize a small interactive system you can publish.
Game Design I built the designer's eye and basic code literacy. This studio turns it into a shippable prototype: AI art & sound pipelines, real GDScript, the advanced Godot systems that hold a small game together, a production process — and a finished entry to a live game jam.
The syllabus
Game Design I. The destination is the August Brackeys jam — a finished game with your name on it, built from a tiny prototype using the systems in Modules 3–4.
1.1 · Prompt → reference → on-model
Anyone gets an image; a designer gets the image, then ten that match it. Write a tight prompt, lock a reference, then generate variations that stay on-model by feeding the reference back in.
- Prompt with constraints — subject + style + framing + background ("flat magenta for keying") + negatives.
- Pick a hero — the one with the right silhouette and soul; it becomes your reference.
- Generate on-model — pass the hero back so new poses keep the same character.
AI does
- render endless drafts
You do
- write constraints, pick the hero, judge on-model
1.2 · Turnarounds & sprite sheets
A game character needs a turnaround (front / 3-4 / side / back), an expression sheet, and key poses (idle, move, hero action). You direct each from your one on-model reference. For 2D, those poses become a sprite sheet the engine flips through.
1.3 · Transparency & importing to Godot
Games need transparency. The DEMOLISH trick: generate on flat magenta, then chroma-key that color to alpha and crop. Then import the PNG into Godot.
mask = (r > 165) & (g < 105) & (b > 165) alpha[mask] = 0 image.crop(image.getbbox())
Load runtime assets with load("res://ship.png") (the imported resource), not raw file reads — or they vanish in the web export.
1.4 · Building a consistent visual set
One great asset is luck; a coherent set is design. Lock a palette, light direction, detail level, and shape language — then hold every generation to them. DEMOLISH's 8 ships cohere because a human enforced one visual language across dozens of drafts.
Generate a 3-asset set (player, enemy, pickup) that clearly belongs to one world. Write the 4 rules you held them to.
2.1 · Procedural sound effects
Game SFX are often synthesized, not recorded — and that's a superpower: a sound built from math is fully tunable. DEMOLISH's explosion = a low sine sweep (body) + noise burst (crunch) + a click (punch). The win: make it parametric — bigger enemy → lower, longer boom — by changing a number.
2.2 · AI music for BGM & stingers
Music models (e.g., Lyria) generate full instrumental tracks from a brief — "epic orchestral space battle, building, loopable, no vocals." DEMOLISH used per-level tracks, a level-up fanfare, and a death theme, all generated this way.
AI does
- compose the audio
You do
- choose which track per moment; sequence the escalation
2.3 · Per-level / per-state audio
Audio should track state: calm early, driving late, a fanfare on level-up, a dirge on death. The engine swaps the track when state changes. This simple system makes a game feel alive — the audio half of escalation (you'll build the state machine in 4.1).
2.4 · Mixing & when sound is "too much"
The hardest audio skill is restraint: balance volumes so SFX punch through music, and resist juicing every event. Like screen shake, sound has a sweet spot — past it, the game is noise. Playtest with sound on.
3.1 · Variables are knobs
A variable is a named value the game reads; change it, change the experience. They're your design parameters. The key move for designers: mark a variable @export and it shows in Godot's Inspector as a slider/field — tune the feel by dragging, no code touched.
@export var speed: float = 220.0 # appears in the Inspector @export var charge_time: float = 1.2 var lives = 3
Live — edit a knob, press Run, click the game:
Add 3 @export variables to a script and tune them in the Inspector while the game runs. Code variable = adjustable design parameter.
3.2 · Functions are behaviors
A function is a named behavior — a verb the game can do, not math. shoot(), explode(), take_damage(). You call it by name and the behavior happens.
func take_damage(amount): health -= amount flash_red() if health <= 0: die()
Write one behavior function for your game and call it from a button or a hit.
3.3 · If-statements are rules
An if is a rule: when X, do Y. Rules are where game design lives — win, lose, reward, boundary. The formal "rules" you designed are written here.
if health <= 0: die() if score >= goal: win() if position.x < 0: position.x = 0 # boundary
Live — this enemy follows an if/elif/else ladder. Click to damage; edit the thresholds + Run:
Add one failure condition and one reward condition to your game.
3.4 · The game loop
Every game is one loop ~60×/sec. Godot gives you three hooks:
_ready()— setup, runs once (place things, start music)._process(delta)— every frame (UI, timers, non-physics motion)._physics_process(delta)— every physics frame (movement, collisions — steadier).
func _physics_process(delta): position += velocity * delta # delta keeps speed steady on any PC
Live — the loop drives this enemy via _process. Edit move_speed, Run, click it:
3.5 · Nodes, scenes, signals
This is what Godot really is — and it maps to product design:
- Node = a part (sprite, sound, timer, collision area).
- Scene = a reusable assembled component made of nodes (the "player", an "enemy") — build once, instance many times. Like a product module.
- Signal = an event a node fires that others react to (
pressed,body_entered,timeout) — like a sensor.
# when something enters this Area2D, take damage func _on_area_body_entered(body): body.take_damage(1)
Make an enemy a reusable scene; spawn three instances of it.
3.6 · AI coding workflow
Direct AI like a designer, in a fixed format — and make it change small pieces, never rewrite the project.
INTENT: the feeling / outcome you want CURRENT: what it does now DESIRED: what it should do instead CONSTRAINT: change ONLY the X function; explain the change ERROR: paste the exact error message (when debugging)
- Ask for one function at a time; have it explain what it changed.
- When it breaks, paste the exact error back — don't describe it.
- Never accept a full-project rewrite. Read & playtest every change.
4.1 · State machine
A game is in one state at a time — and the loop behaves differently in each. This is just a product's user-journey / state diagram, in code.
enum {TITLE, PLAYING, LEVEL_UP, DEAD} var state = TITLE func _process(delta): match state: PLAYING: update_play(delta) LEVEL_UP: show_banner(delta) DEAD: pass # wait for restart
Title screens, level-up banners, death screens, music swaps — all fall out of the state. Draw your states as a diagram first (you already know how).
4.2 · Components — don't put everything in main.gd
As a game grows, one giant script becomes unmanageable. Split behavior into components — small reusable nodes/scripts, each doing one job: Health, Movement, Shooter, Feedback. Attach what each object needs. This is modular product design: separable parts with clear interfaces.
Pull "health + take damage + die" out of your main script into a reusable Health component.
4.3 · Data-driven design
The most designer-friendly "advanced" code: drive content from a table, not from scattered code. Levels, enemies, difficulty curves become data you can read and tune at a glance.
var levels = [ {"ships": 2, "speed": 120, "music": "calm"}, {"ships": 4, "speed": 180, "music": "tense"}, {"ships": 6, "speed": 240, "music": "epic"}, ]
Now your whole difficulty curve is a few rows you can edit like a spreadsheet — design through data. Arrays, Dictionaries, and Resources are the tools.
Live — a data-driven world. The values below drive it; edit + Run:
Write your game's levels (or waves) as a table and have the code read it.
4.4 · Feedback manager
Don't scatter shake, flash, hit-stop, sound, and particles across the codebase. Centralize them into one reusable feedback system you call from anywhere: Feedback.hit(strength). "Advanced" here isn't algorithms — it's a clean, reusable feel toolkit.
func hit(strength): shake(strength) flash(strength * 0.03) freeze(strength * 0.004) # hit-stop play("boom", strength)
Live — one feedback call, all the juice. Edit the params, Run, click:
4.5 · Spawning & timers
Rhythm and pacing are code. Use a Timer node or await to spawn waves, stagger enemies, and pace events. This is where you design tempo.
func spawn_wave(n): for i in n: spawn_enemy() await get_tree().create_timer(0.4).timeout # stagger
4.6 · Save, restart, settings
The difference between a toy and a product: it remembers, recovers, and respects the player. Add a high-score save, a clean restart, a pause, and a volume setting.
get_tree().reload_current_scene() # clean restart var f = FileAccess.open("user://save.dat", FileAccess.WRITE) f.store_var(high_score)
4.7 · Export to web (the ship step)
- Add a Web export preset: Project → Export → Add → Web. Single-threaded preset = simplest hosting.
- Export to a folder →
index.html+.wasm+.pck+.js. - Test locally with a tiny server (never double-click
index.html— browsers block it). - Host on itch.io (easiest; upload the zip, tick "play in browser") or GitHub Pages (handles the ~35MB engine wasm).
Two gotchas that break web builds (DEMOLISH hit both):
- Load assets with
load("res://..."), not rawFileAccessreads — or they vanish in the pack. - Cloudflare Pages rejects files >25MB (the wasm is bigger) — use itch.io or GitHub Pages.
Always play the hosted build before calling it shipped.
5.1 · Ideation → pre-production → production → ship
Lemarchand's A Playful Production Process (USC) splits a project into four stages: ideation (explore), pre-production (find the core, make a vertical slice), production (build out from a locked design), ship (polish, fix, release). Each ends in a milestone that forces a decision.
5.2 · Scope ruthlessly — the vertical slice
The number-one killer is scope. The antidote is the vertical slice: one small piece, fully finished — playable, juiced, shippable. If the slice isn't fun, the full game won't be. Build it first, then expand.
Small & complete > big & broken.
5.3 · Doing every role — solo, with AI
A studio splits roles across departments; a solo dev with AI plays all of them. The bottleneck shifts from "can I make the art/code/sound?" (AI does) to "can I direct them into one coherent game?" That integration is the job — and why a designer's eye matters more than ever.
5.4 · Milestones & cutting your darlings
Set hard milestones (slice done / feature-complete / shipped) and protect them by cutting. Every feature you cut buys time for the ones that matter. Cutting things you love — when they don't serve the experience goal — is the mark of a professional.
6.1 · How a game jam works
A jam is a timed make-a-game event: a theme drops, you build a complete small game in 48 hours to a week, then everyone plays and rates each other's entries. It's the playcentric loop compressed — with a built-in audience.
- GMTK Jam — 4 days, huge, beginner-OK.
- Brackeys Jam — 1 week, the new-maker gold standard our target · August
6.2 · Scope for the clock (and sleep)
48 hours is wall-clock, not work-hours — you sleep. Tired makes bad creative and bad code. The finishers aren't the ones who skip sleep — they're the ones who scope small enough to sleep. Submit early; servers crash at the deadline.
6.3 · The 48-hour dry run
Before the real jam, rehearse: invent a theme, build a tiny complete game in 48 hours, ship it. The goal isn't greatness — it's to find your weak points (over-scope? pipeline jams?) while the stakes are zero.
Random one-word theme. Build & ship something playable in 48 hours. Note every place you got stuck.
6.4 · Postmortem of the dry run
Right after, write what went right, wrong, and what you'll change for the real jam. The cheapest lesson you'll ever buy — mistakes here cost nothing and won't repeat when it counts.
7.1 · Theme drop → concept
The jam begins. Resist the first idea (everyone has it). Brainstorm, then choose a concept with a clear experience goal and one core verb — small enough to finish, distinct enough to stand out.
7.2 · Build the vertical slice
Get to a playable core fast — the one verb working, on one screen. Everything you've learned runs here: paper-check the mechanic, build it in Godot with AI, generate assets and sound, drive levels from data, keep a playable version at all times.
7.3 · Juice, balance, ship & submit
Final hours: juice the key moment, tune difficulty, add a title and clear win/lose, export to web, and submit early. Write a one-paragraph store page. Then play and rate other entries — that's how the community plays yours back.
You will have a finished, original game with your name on it.
8.1 · Writing the postmortem
After the jam, write the postmortem: what went right, what went wrong, what you learned. Industry standard, and where the real learning crystallizes. Be honest — a clear-eyed postmortem signals a serious designer.
8.2 · Documenting process as portfolio
For a student, the process is the portfolio: experience goal, paper prototypes, asset iterations, playtest notes, the postmortem — plus the playable link. A shipped small game with thoughtful documentation beats a big unfinished prototype, and is what a USC IMGD application wants to see.
8.3 · What's next — Game Design III
With one shipped game and a real production cycle behind you, the next step deepens your design voice: bigger systems, information & signaling games, and a longer-form project aimed at Steam. But first — finish this one. Done > perfect.