CS 276 // GAME SYSTEMS IN GODOT

Juice It.

Game Feel, the Lab 5 Launch, and Sprint 2

Lucas P. Cordova, Ph.D.

Willamette University  |  Thursday, October 22, 2026

SCORE 000276HI 999999

Outline

  • Save point: the quest log.
  • Feel: the same game twice, and why one of them feels alive.
  • The toolkit: tweens, shake, hit stop, particles, and sound, in Godot.
  • Lab 5 launch: juice your platformer, or the kit.
  • Rituals: close Sprint 1, plan Sprint 2, on your laptops.
  • Game over screen: checkpoint and the quest log.
QUEST LOG

Lab 5 goes out today and is due Thursday, October 29, 9:59 PM. The Sprint 1 report is written in class today and due tonight, 9:59 PM, with the Sprint 2 board.

CS 276
LEVEL 01

Save Point

The quest log

SCORE 000276HI 999999

Quest log

WhenWhatWhere
Tonight, 9:59 PMSprint 1 report (written in class), and Sprint 2 beginsRepo: docs/sprint-1.md and FEEDBACK.md; URL on Canvas
Tonight, 9:59 PMPeer evaluation for Sprint 1Canvas
Thu Oct 29, 9:59 PMLab 5: game feel (out today)Push to main
Thu Oct 29, in classPlaytest day: the whole class plays every gameBring a build
Thu Nov 5Alpha gate and Stakeholder Review 2All MUSTs in, or formally cut
CHECKPOINT

Today's report answers the review sheets you collected Tuesday: adopt, adapt, or decline, each with a reason.

CS 276
LEVEL 02

Feel

The same game twice

SCORE 000276HI 999999

Same game, twice

LIVE DEMO

Watch the Bearcat jump, land, and hit a spike. Then again.

  1. M1. Run the project. Jump twice, touch the spike.
  2. Stop. M3. Accept the reload prompt. Run it. Same jumps, same spike.
  3. Fingers up: how many things changed between the two runs?
PATTERN

Speed, gravity, jump velocity, the level, the spikes: identical. Every number that decides where the Bearcat lands is the same in both builds.

  • Stretch on takeoff, squash on landing, a puff of dust.
  • On the spike: the world freezes, the sprite flashes, the screen jolts, then home.
  • Five changes, and the second game is the one people want to keep playing.
CS 276

Game feel

DEFINITION

Game feel is the real-time control of a virtual object, in a simulated space, with that interaction emphasized by polish [1].

ALSO

Juice is the polish part: a lot of output for a little input. The player presses one key and the game answers with motion, sound, and light, all at once [2].

  • The mechanics answer what happened. Juice answers it louder and faster, so the player never has to ask.
  • Players report juice as "it feels good" and its absence as "it feels floaty," and almost never as anything more specific.
CS 276

Six channels

ChannelWhat it movesToday, in Godot
MOTIONThe thing itself: squash, stretch, bounceTween on the sprite
TIMEThe clock: hit stop, slow motionEngine.time_scale
SCREENThe camera: shake, zoom, flashCamera2D.offset
PARTICLESDebris: dust, sparks, confettiCPUParticles2D, one shot
SOUNDA sound per event, never the same twiceAudioStreamPlayer plus pitch wobble
UIThe numbers react: a banner punches in, a label flashesTween on a Control
CHECKPOINT

One moment, several channels. The hit uses three; the jump uses two. That stacking is the whole trick.

CS 276

The rules of juice

  • Feel never changes mechanics. Scale the sprite and leave the body alone. A squash that moves a landing is a bug.
  • Snap in, ease out. The effect is instant; the recovery is the tween. The other way around reads as mush.
  • Stack channels on one moment. One trick is a gimmick. Three tricks on the same frame is a feeling.
  • Every number is a dial. An @export you can turn while the game runs. You will overshoot, then settle.
  • Legible first. Juice tells the player what happened. If it hides the player or eats input, it is noise.
WATCH OUT

Juice does not fix a boring loop. It makes a good loop feel like one. Alpha first, feel second, and both before beta.

CS 276
LEVEL 03

The Toolkit

Tweens, shake, hit stop, particles, and sound, in Godot

SCORE 000276HI 999999

A Tween, in three lines

var tween := create_tween()
tween.tween_property(sprite, "scale", Vector2.ONE, 0.18)
tween.set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
  • A Tween animates one property from wherever it is to a target, over a time, then frees itself.
  • tween_property(object, "property", target, seconds). Chain another call and it runs after; parallel() runs it alongside.
  • Trans picks the curve (linear, sine, back, bounce, elastic). Ease picks which end gets the curve: EASE_OUT is fast then settling.
  • TRANS_BACK with EASE_OUT overshoots the target a little before it lands. That overshoot reads as bounce.
CS 276

Snap in, ease out

func _squash(to: Vector2) -> void:
    if squash_tween:
        squash_tween.kill()
    sprite.scale = to
    squash_tween = create_tween()
    squash_tween.tween_property(
        sprite, "scale", Vector2.ONE, squash_time
    ).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
  • sprite.scale = to is the snap: instant. The tween only brings it back to Vector2.ONE.
  • kill() first. A landing that interrupts a jump would otherwise leave two tweens fighting over one property.
  • Only sprite scales. The body and its collision shape never hear about it: rule one.
  • jump_squash, land_squash, and squash_time are exports. Dials.
CS 276

The door is where it lives

func _change_state(new_state: State) -> void:
    var old_state := state
    state = new_state
    sprite.texture = TEXTURES[new_state]
    if old_state == State.AIR and new_state != State.AIR:
        _land()

func _jump() -> void:
    velocity.y = jump_velocity
    _squash(jump_squash)
    _play(jump_sound)

func _land() -> void:
    _squash(land_squash)
    dust.restart()
    _play(land_sound)
PATTERN

The door is the one place a state change happens, so it is the one place an entry effect belongs. Landing is "coming from AIR into anything else."

CHECKPOINT

Three jump lines became one _jump(), so every jump (floor or air) gets the same squash and the same sound.

CS 276

Shake is a camera job

extends Camera2D
## Screen shake. One job: jitter the camera's offset, then settle.

@export var decay: float = 60.0   # pixels of shake lost per second

var strength: float = 0.0

func shake(amount: float) -> void:
    strength = amount

func _process(delta: float) -> void:
    if strength <= 0.0:
        offset = Vector2.ZERO
        return
    offset = Vector2(randf_range(-strength, strength),
        randf_range(-strength, strength))
    strength = move_toward(strength, 0.0, decay * delta)
  • offset nudges the view without moving the camera's position. It exists for this.
  • Each frame: a random point inside a square of the current strength.
  • move_toward walks the strength to zero at decay pixels per second, then the offset snaps home.
  • One job, one script, one public method: shake(amount). The brain calls it; the camera decides nothing.
CS 276

Stopping time

func hit_stop(scale: float, seconds: float) -> void:
    Engine.time_scale = scale
    await get_tree().create_timer(seconds, true, false, true).timeout
    Engine.time_scale = 1.0
WATCH OUT

Engine.time_scale = 0.05 slows every clock in the game, including this timer. A timer counting scaled seconds would take over two real seconds to fire, and the game would sit frozen. The fourth argument, ignore_time_scale = true, makes this one timer count real seconds.

  • await parks the function until the timer fires, then the last line runs. Nothing else waits.
  • The same function with 0.3 for half a second is slow motion. Two moods, one line of code.
CS 276

The hit, in order

func respawn() -> void:
    if respawning:
        return
    respawning = true
    velocity = Vector2.ZERO
    died.emit()
    _flash()
    _play(hit_sound)
    await get_tree().create_timer(
        flash_time, true, false, true).timeout
    position = spawn_point
    respawning = false
    _change_state(State.AIR)
func _on_player_died() -> void:
    deaths += 1
    hud.show_deaths(deaths)
    camera.shake(hit_shake)
    hit_stop(hit_stop_scale, hit_stop_time)
  • died.emit() fires first, so the brain freezes time and shakes the camera before the Bearcat moves.
  • Flash, sound, then wait on the same real-seconds timer. The teleport lands exactly as time resumes.
  • respawning guards the door: the spike can fire again while the body sits on it during the freeze.
CS 276

Dust and sound

func _flash() -> void:
    sprite.modulate = flash_color
    var tween := create_tween()
    tween.tween_property(sprite, "modulate", Color.WHITE, flash_time)

func _play(sound: AudioStreamPlayer) -> void:
    sound.pitch_scale = randf_range(
        1.0 - pitch_wobble, 1.0 + pitch_wobble)
    sound.play()
  • Particles: a CPUParticles2D with One Shot on and Emitting off; dust.restart() fires one burst. Set in the Inspector, fired from the door.
  • Sound: one AudioStreamPlayer per event, a child of the thing that makes the noise. The plain player is right for one screen; the 2D one fades with distance.
  • Pitch wobble: the same sample at a random pitch between 0.9 and 1.1 stops sounding like a sample.
PATTERN

The flash is a tween too: snap to magenta, ease back to white. It runs on the scaled clock, so it holds through the freeze and fades after.

CS 276

Containers own their children

WATCH OUT

A Label inside a VBoxContainer cannot be scaled by a tween. The container sets its children's position, size, rotation, and scale on every layout pass, and puts them straight back.

Want to animate a Control's...Do this
modulate (color, alpha)Safe anywhere. Containers never touch it.
scale or rotationAnchor it to the root directly, outside any container, and set its pivot offset to its center.
positionSame: outside the container, or tween the container's own position.
CHECKPOINT

The CLEAR banner in Lab 5 sits outside the column for exactly this reason. It cost me twenty minutes; it will cost you zero.

CS 276
LEVEL 04

Lab 5 Launch

Juice your platformer, or the kit

SCORE 000276HI 999999

Lab 5: Game Feel

Option A: the kitOption B: your own Lab 3
Start fromA finished Lab 3 platformer (M4 plus double jump), in the Lab 5 repoYour Lab 3 files, copied into the Lab 5 repo
Pick it ifYour Lab 3 stopped before M4, or you want a known good baseYour spikes respawn and your flag clears
ThenBuild M1 to M5 from the guideBuild M1 to M5 from the guide, substituting your names
GradeIdenticalIdentical
QUEST LOG

Due Thursday, October 29, 9:59 PM. Solo. Accept the Classroom50 link on Canvas, clone, and open GUIDE.md. Sounds and a dust particle ship in assets/.

CS 276

Milestones

  • M0: pick a start, run it. Find the three spots in player.gd that get juiced.
  • M1: the shell. HUD (time, deaths), a brain on the main scene, pause menu, clear screen, title screen. Week 7, on your game.
  • M2: the jump. Squash and stretch through the door, landing dust.
  • M3: the hit. Camera shake, hit stop, flash, delayed respawn. Freeze, flash, shake.
  • M4: sound. Four events, pitch wobble on at least one.
  • M5 (required): your signature. One more moment, two channels, from the menu or your own.
CHECKPOINT

The feel rule, graded: every juice number is an @export, and none of them change a gameplay number.

CS 276

The contract

CriterionPoints
The shell: title, HUD, pause with Resume / Restart / Title, clear screen; title is the main scene25
The jump: squash and stretch on jump and landing, a tween on the sprite only, landing dust15
The hit: flash, camera shake from a camera script, hit stop that returns to 1.0, in the right order20
Sound on four events, pitch variation on at least one10
Your signature: one more moment, two channels, named in the README10
Feel in dials: juice numbers are exports, gameplay numbers untouched, class code style5
README's Your Submission section: screenshots, feel sheet, one dial, signature, surprise15
CS 276
LEVEL 05

Rituals

Close Sprint 1, plan Sprint 2

SCORE 000276HI 999999

A sprint has rituals

DEFINITION

A ritual is a meeting or a document that happens at the same point of every sprint, so the team never has to decide whether to do it. Scrum calls them events [8].

RitualWhenArtifact
Sprint planningFirst day of the sprintThe board: issues labeled, owned, ordered
The boardEvery work sessionCards move; Done means it runs on main
Review gateMid-sprint ThursdayTwo printed review sheets, a tagged build
Feedback logThe week after the gateFEEDBACK.md: adopt, adapt, or decline, with a reason
Sprint reportLast day of the sprintdocs/sprint-N.md: shipped, slipped, learned, next
Peer evaluationWith the reportCanvas form; the commit history is the evidence
CS 276

Why the rituals matter

  • Planning turns a wish list into a list a team of two can finish in three weeks. Without it, the backlog decides for you.
  • The board is the status report. A spoken "almost done" is worth nothing; a card in Done that runs on main is worth everything.
  • The gate is a deadline you cannot negotiate with, which is the point. Studios ship because of milestones.
  • The feedback log is where stakeholders find out they were heard, including when the answer is no.
  • The report is the retrospective: what slipped, and what you will do differently. Same-mistake-twice is the thing it prevents.
CHECKPOINT

Rituals cost a team of two about an hour a sprint. Skipping them costs the last week of the semester.

CS 276

Close Sprint 1

CO-OP MODE

Five minutes, with your teammate, on the board:

  1. Move every finished card to Done. Finished means it runs on main from a fresh clone, so pull and check.
  2. Every card left in Todo or In Progress gets one line in its body: why it slipped.
  3. Lay the two review sheets from Tuesday next to the laptop. You will need them in a minute.

Nothing gets deleted. Slipped cards are the first candidates for Sprint 2.

CS 276

Sprint 2: the goal

DEFINITION

Sprint 2 runs October 22 to November 12. Its goal is the alpha: every MUST in the proposal is in the game, or formally cut. The alpha gate and Stakeholder Review 2 are Thursday, November 5; the Sprint 2 report is due November 12.

The alpha gate checksSo the board needs
Every MUST in, or cut in writingOne card per MUST, or a line in the proposal saying it moved to WANT and why
A complete session: start, play, win or lose, restartCards for the title, the end state, and the restart, if any are missing
FEEDBACK.md answering Review 1A card per adopted note; declined notes need no card, only a reason
Five minutes of play without a crashA Stability pass card, owned, in the last week
Tag alpha, both members in the historyTwo cards: Tag alpha, and whichever teammate has fewer commits owns more
CS 276

Sprint planning, the process

  1. Start from the sheets. Each stakeholder note gets a verdict in FEEDBACK.md: adopt, adapt, or decline, with one reason. Adopted and adapted notes become issues.
  2. Pull the MUSTs. Every MUST from the proposal that is not on the board yet becomes an issue: a player-facing title and a Done when list.
  3. Carry the slipped cards. Keep them, split them if they slipped because they were too big.
  4. Count the sittings. Two people, three weeks, about twelve sittings of real work. If the cards need more, something moves to WANT today, in writing.
  5. Order the top. Alpha checklist rows first, then adopted feedback, then juice. Label sprint-2, assign one owner per card.
  6. Read the board as the report. The Sprint 2 board is the Next sprint section of tonight's Sprint 1 report.
CS 276

Co-op: plan Sprint 2

CO-OP MODE

Twenty minutes, both laptops on the team repo:

  1. One teammate writes FEEDBACK.md from the two sheets while the other turns the proposal's MUSTs into issues.
  2. Trade. Each reads the other's work and adds the Done when lists.
  3. Count sittings. Cut to twelve, in writing.
  4. Label sprint-2, assign owners, order the Todo column: alpha rows on top.
  5. Round clear: show me a board where I can read the alpha gate off the top five cards.

The Sprint 1 board stays as it is. Sprint 2 is a new label and a fresh Todo order on the same project.

CS 276

Sprint check

On the boardWhat it means, and the fix
No card mentions a stakeholder noteThe sheets were not read. Every adopted note is a card whose body quotes the note.
A MUST with no card and no written cutIt is neither in nor out. Decide now; the alpha rubric counts both.
Twenty cards labeled sprint-2More than twelve sittings. Move the bottom of Todo to WANT and say so in the proposal.
Every card on one nameOne teammate ships and one watches. Reassign until both own alpha rows.
No Stability pass, no Tag alphaAdd both. Five crash-free minutes is a rubric line.
Juice cards above alpha cardsReorder. Feel is Lab 5's job this sprint; the alpha is the game's.
CS 276

The Sprint 1 report, tonight

Due tonight, 9:59 PM: docs/sprint-1.md in the team repo, with FEEDBACK.md at the root, and the repo URL on Canvas. Short. Pulled from the board you just built.

SectionWhere it comes from
ShippedThe Done column: closed issues, as a changelog.
SlippedThe cards you annotated in the closeout, with the one-line reasons.
LearnedStakeholder Review 1 and Tuesday's playtests: what you saw people do, and what you will do differently.
Feedback logA pointer to FEEDBACK.md: every note marked adopt, adapt, or decline, with a reason.
Next sprintThe Sprint 2 board: the top five cards, and any MUST that moved to WANT.
QUEST LOG

Alongside the report, each of you files a peer evaluation of your teammate on Canvas. The commit history is the evidence.

CS 276
LEVEL 06

Game Over Screen

What you know now

SCORE 000276HI 999999

Checkpoint reached

CHECKPOINT
  • Juice is a lot of output for a little input. The mechanics say what happened; the juice says it louder and faster.
  • Six channels: motion, time, screen, particles, sound, UI. Stack several on one moment.
  • Snap in, ease out. A tween brings things back; it never starts them.
  • The door is where entry effects live. Shake is a camera job. Time stops with one line and a real-seconds timer.
  • Feel never changes mechanics, and every number is a dial.
  • A sprint runs on rituals: plan, board, gate, feedback log, report, peer evaluation. The board and the report are the same thing at two moments.
QUEST LOG

Tonight, 9:59 PM: Sprint 1 report, FEEDBACK.md, peer evaluation. Thu Oct 29: Lab 5 due 9:59 PM, and playtest day in class. Thu Nov 5: the alpha gate.

CS 276

Sources

  • [1] Swink, Game Feel: A Game Designer's Guide to Virtual Sensation (Morgan Kaufmann, 2009).
  • [2] Jonasson and Purho, Juice It or Lose It (talk, Nordic Game Indie Night, 2012).
  • [3] Nijman, The Art of Screenshake (talk, INDIGO Classes, 2013).
  • [4] Brown, Secrets of Game Feel and Juice, Game Maker's Toolkit (video essay, 2015).
  • [5] Thomas and Johnston, The Illusion of Life (Disney, 1981): the twelve principles, squash and stretch first.
  • [6] Godot docs: Tween, CPUParticles2D, Camera2D, AudioStreamPlayer, Engine.time_scale, SceneTree.create_timer.
  • [7] Lab 5 repo: GUIDE.md, milestones M0 to M5.
  • [8] Schwaber and Sutherland, The Scrum Guide (2020): sprint planning, review, and retrospective.
  • [9] Keith, Agile Game Development, 2nd ed. (Addison-Wesley, 2020): sprints and milestones in game studios.
CS 276