Exercise 9 — Curves & Locks
GAME 1303-001 · Unity 6.x · Week 13 — Tuesday, November 17
In class, 10:30 – 11:45 · 2 points · Submit by end of class
📘 Your Game Design Doc is due in Canvas tonight — see assignment-game-design-doc.md
The skill
Two ideas, one exercise, because they are the same idea.
Difficulty curve. Games get harder. How they get harder is a designed shape, not a side effect. Linear ramps are exhausting; a curve with peaks and recoveries is what keeps people playing.
Lock and key. A challenge the player can only pass by understanding something. Not a bigger enemy — a different problem, whose solution is knowledge.
The connection: difficulty should increase because the player is being asked to know more, not merely to react faster. A game that only speeds up eventually excludes everyone. A game that keeps teaching stays interesting.
By the end of the block you will have: health and score wired, a difficulty ramp you graphed before you tuned, and one puzzle whose solution is understanding.
What you need
- Your own game project
HelperScripts.unitypackage— you needHealth,Score,Spawner,ObjectPool
Part 1 — Health and score (15 min)
1. Add Health to the player
| Field | Set to |
|---|---|
maxHealth | 3 |
invulnerabilityTime | 0.5 |
destroyOnDeath | ✘ (off for the player) |
Hook the events in the Inspector:
OnDamaged→HitFlash.Flash()and your hurt soundOnDeath→GameStateManager.Lose()
invulnerabilityTimeis not a nicety. At 0, standing inside a hazard drains three hearts in three frames and the player cannot tell what killed them. This is the single most common "my game is broken" bug in this course.
2. Wire hazards to it
On each hazard's TriggerZone, set On Enter () → Health.TakeOneDamage().
3. Add Score
Put a Score component on your GameManager. On any pickup's TriggerZone, wire On Enter () → Score.AddTen().
If you built the HUD in Ex 7, both now display live with no extra work.
✅ Checkpoint 1 — taking damage flashes, costs health, and eventually loses; pickups add score.
Part 2 — Graph the curve before you build it (15 min)
4. Draw it on paper
Horizontal axis: time, 0 to 5 minutes. Vertical: difficulty, 0 to 10.
Draw the shape you want. Then check it against these:
| Shape | What it does |
|---|---|
| Straight line up | Feels relentless. Players quit at the point it passes their skill, and nothing invites them back |
| Staircase | Steps up, then holds. The hold is where players get good at the new thing. Most commercial games |
| Sawtooth | Rises, drops, rises higher. The drops feel like mastery — the same challenge is now easy |
| Flat | Boring by minute two, regardless of how good the mechanic is |
Draw a staircase or a sawtooth. Mark where the player recovers. Recovery beats are what make the peaks readable — the same principle as rest beats in Ex 5, applied to time instead of space.
Photograph it. The graph is part of your submission, and it must be drawn before you tune.
✅ Checkpoint 2 — a difficulty graph with marked peaks and recoveries.
Part 3 — Build the ramp (15 min)
5. Set up the spawner
Empty GameObject → add ObjectPool (assign your enemy/obstacle prefab, initialSize 20) and Spawner.
| Field | Set to |
|---|---|
mode | Continuous or Waves |
spawnInterval | 2.0 |
rampRate | 0.02 |
minimumInterval | 0.35 |
maxAlive | 12 |
6. Understand what rampRate does
Each spawn shortens the next gap by that fraction. At 0.02, the interval halves after about 35 spawns — roughly a minute in. At 0.1, it bottoms out in fifteen seconds.
rampRate is your difficulty curve. It is not a technical setting. Change it and the entire feel of your game changes.
minimumInterval is the floor. Without it the ramp eventually spawns every frame and the game becomes unplayable rather than hard — which is a different thing, and never the goal.
7. Play for three minutes and compare
Play with a stopwatch. Note where you struggled. Plot the actual difficulty against your drawn curve.
Almost nobody matches their graph on the first attempt. The usual gaps:
| What happens | Fix |
|---|---|
| Fine for 90 seconds, then instantly impossible | rampRate too high; try halving it |
| Never gets hard | rampRate too low, or maxAlive capping it |
| Hard but shapeless | You need Waves mode — continuous spawning has no recovery beats in it |
Recovery is what Waves mode gives you. waveInterval is the gap between waves, and that gap is where the player breathes.
✅ Checkpoint 3 — ramp tuned to approximate your drawn curve.
Part 4 — One lock and key (15 min)
8. Build a challenge that requires knowing something
Not a bigger enemy. A different problem.
| Type | Example |
|---|---|
| Mechanical | An enemy only vulnerable while dashing — the player must know dash grants invulnerability |
| Spatial | A gap only crossable by dashing off a wall — knowledge of a property, not a stat |
| Sequential | Three switches that must be hit in order, and the order is shown somewhere in the room |
| Resource | A door that costs 50 score — the player chooses between spending and saving |
| Observational | A safe path marked by a detail established earlier in the level |
9. Teach it, then test it
Every good lock is taught before it is tested. The pattern is always:
- Teach — introduce the idea somewhere safe, where failing costs nothing
- Test — require it, where failing costs something
- Twist — require it combined with something else
Build at least steps 1 and 2. If you only build the test, players will call your puzzle unfair, and they will be right — an untaught lock is a guess.
10. Write your notes
Docs/Ex09_notes.txt:
Health: 3, invulnerability 0.5s, flash + sound on damage.
Score: 10 per crystal, combo window 2s.
Curve drawn: sawtooth — peaks at 0:45, 2:00, 3:30, recovery after each.
Curve achieved: rampRate 0.015, Waves mode, waveSize 5, waveInterval 7.
Difference from my graph: my first draft used rampRate 0.05 continuous.
It was flat-out impossible at 90 seconds and there was no recovery
anywhere, so the peaks didn't read as peaks — it was just a wall.
Switching to Waves gave me the drops, and the peaks suddenly landed.
Lock: the shielded enemy. Only takes damage while I'm dashing.
Teach — one appears alone in a safe corridor at 0:30, no other
threats, so you can experiment until you work it out.
Test — two appear at 2:10 with hazards between them, so you have
to dash through the gap AND land the hit.
What I learned: I originally put the shielded enemy in the first wave.
Three testers all just ran past it and never learned the rule, so the
2:10 test felt unfair. Moving the teach beat to an empty corridor
fixed it completely.
✅ Checkpoint 4 — a lock with a teach beat and a test beat, notes comparing drawn curve to achieved curve.
Part 5 — Build and submit (5 min)
11. Build into MyGameName/PC_Builds/Ex09/, then submit
Zip as lastname_ex09.zip: the build, your difficulty graph photo, and Docs/Ex09_notes.txt.
Canvas → Assignments → Exercise 9.
📘 Game Design Doc due tonight, 11:59 PM, separately.
Rubric — 2 points
| Criterion | Pt | Met when |
|---|---|---|
| Implementation | 1 | Build runs · health with invulnerability, damage feedback, and death · score wired · spawner ramps over time · one lock built with both a teach beat and a test beat · no console errors |
| Design decision | 1 | Difficulty graph drawn before tuning and submitted · notes compare drawn curve to achieved curve and explain the difference · the lock's solution is knowledge, not reflex |
Troubleshooting
I die instantly touching anything.
invulnerabilityTime is 0. Set it to 0.5. This is the bug.
Health goes down but nothing shows.
OnDamaged isn't hooked to HitFlash.Flash(). Both an object and a function must be selected in the UnityEvent.
The game slows to a crawl after a minute.
You're instantiating instead of pooling. Assign the ObjectPool reference on the Spawner.
Enemies stop spawning entirely.
maxAlive is reached and nothing is dying — or Health.OnDeath isn't firing, so the alive count never drops.
The ramp does nothing.
rampRate is 0, or you're in Waves mode, which uses waveInterval rather than the continuous ramp.
Score resets when I restart.
That's correct — score is per-run. The high score persists in PlayerPrefs.
My puzzle is "too hard" according to everyone.
It's almost never too hard. It's untaught. Add the teach beat.
If you finish early
- Set
rampRateto 0.1 and play. Note the exact second it becomes impossible — that number is useful. - Build the twist: your lock combined with a hazard.
- Give your lock a second solution and watch which one testers find.
Before next week
Ex 10 — Test & Iterate is the last exercise. You will put your game in front of three real people and fix what breaks. Bring a build that runs — you cannot do next week's exercise without one.
Week 15 is the Game Build milestone, worth 25 points. Build early.