While True: Play

Lecture W2T1: The Game Loop, Then Paddles

Lucas P. Cordova, Ph.D.

Willamette University

September 1, 2026

Insert Coin

Today’s agenda

LECTURE W2T1 // THE GAME LOOP, THEN PADDLES

The plan:

  1. Meet the cast (2 minutes) - Pac-Blitz signed its rivals over the weekend.
  2. The machine under the game (~25 minutes) - the loop, delta, and the two clocks. Just enough theory to build with.
  3. Build (the rest of class) - Lab 1: Paddle Game starts now, here, with me in the room.

Housekeeping: Lab 0 is due tonight, 9:59 PM.

The cast is set

Four rivals, four personalities:

  • The Linfield Wildcat - the chaser. Runs straight at Blitz. Relentless. Very Linfield.
  • The Puget Sound Logger - the ambusher. Fells the corridor ahead of you.
  • The George Fox Bruin - the flanker. A bigger bear stalking a bearcat.
  • The Pacific Boxer - the wildcard. Swings wild, wanders off. Nobody knows.

Also canon: figs trigger the Fig Break - for a few seconds the chase flips and the rivals run from you.

Why the cast matters today

It is all in the pac-blitz README as of this morning - and each of those four personalities is a brain we will actually write in the state-machines weeks. A brain that has to think every single frame. Time to meet the frame.

The Loop

Every game is a while loop

Strip away the genre and the graphics card, and every game since Pong is:

while running:
    read_input()
    update_world()
    draw_everything()
  • One trip through = a frame. Screens want about 60 per second, so the whole trip - input, every script, physics, drawing - must fit in ~16.6 ms.
  • Blow the budget → dropped frames → stutter. Game programming is real-time programming.
  • But look at blitz.gd: there is no while anywhere.

The engine calls you

  • Godot owns the loop - written once, in C++, better than ours would be.
  • Instead of you calling the engine, the engine calls you: once per frame, every node’s _process(delta) runs.
  • That is the Update Method pattern from your reading: each object updates itself one frame; the loop just asks everyone in turn.
  • Nystrom’s phrase - the Hollywood principle: “Don’t call us, we’ll call you.” Every callback you will write this semester (_process, _ready, signal handlers) is a hook into this one cycle.

Delta Time

The units cancel

Thursday we discussed how * delta and Blitz’s speed changed with the machine. Here is the same line with its units written out:

position += direction * speed * delta
  • speed = 260 pixels per second - a rate.
  • delta = seconds since the last frame - a slice of time the engine hands you.
  • pixels/second × seconds = pixels. The units cancel. That is the entire trick.
  • Without delta, speed silently means pixels per frame - and nobody agreed how long a frame is.

Same speed on any machine

Machine Frame rate delta Moved this frame Moved per second
Gaming laptop 240 fps 0.004 s ~1.1 px 260 px
Lab machine 60 fps 0.017 s ~4.3 px 260 px
Dying potato 30 fps 0.033 s ~8.7 px 260 px

Fast machines take many small steps; slow machines take few big ones; everyone arrives together. Never ship motion without delta - your paddle and your ball both obey this rule.

Two Clocks

Physics hates surprises

  • Engines run physics on its own fixed clock: Godot steps physics 60 times a second, every machine, no matter what the renderer manages.
  • That clock has its own callback: _physics_process(delta) - called every physics tick, delta always the same.
_process _physics_process
Called per rendered frame per physics tick
delta varies constant (1/60 s)
Use for visuals, UI, input polling, your own movement math moving physics bodies, anything the physics engine resolves

Which clock does the paddle game use?

_process. And that is the honest answer, not a shortcut:

  • Today’s game does its own math - you move positions and bounce by flipping a velocity’s sign. No physics engine involved, so the render clock is the right clock.
  • Rule of thumb for later: the moment the physics engine resolves your movement (move_and_slide, real collisions), the code moves to _physics_process.
  • That moment is Thursday: velocity + move_and_slide(), collision layers and masks - the ghost stops eating our marionberries, and your paddle game’s arithmetic becomes real physics.

Probe the loop

The proof is runnable. The demo code lives on a branch of the reference repo:

git checkout W2T1-demo    # in your pac-blitz clone

On that branch, blitz.gd wiretaps both clocks:

func _process(delta: float) -> void:
    print("frame:   ", delta)
    ...

func _physics_process(delta: float) -> void:
    print("physics: ", delta)

Run it and read the Output panel: physics: is the same number every line - the fixed clock, right there. frame: wobbles - the render clock, living its life. Two clocks, live.

Two more experiments

  1. Starve the loop: Project → Project Settings, search “max fps” (Application → Run → Max FPS), set 15. Run again: Blitz moves at the same speed, just choppier - frame: deltas tripled and the movement math absorbed them. physics: never flinched. Set it back to 0.
  2. Bend time: one commented line waits in main.gd on the demo branch:
func _ready() -> void:
    # Demo 3 - bend time: uncomment, run, and enjoy global slow motion.
    # Engine.time_scale = 0.3

Uncomment it, run: the whole world in slow motion, one number. It works only because every moving thing multiplies by delta.

Run all three on your paddle game during studio time.

Boss Fight: Lab 1

Your first complete game

Lab 1: Paddle Game. A paddle you control, a marionberry that flies and bounces, a rally to keep alive - and at least one twist that makes it yours.

  • Everything it needs, you already have: the scene recipe, _process + delta, Input, clamp, and the signal doorbell from the marionberries.
  • The starter repo has the court, the sprites, and GUIDE.md - a milestone-by-milestone build guide with the code, plus Dials and Your call boxes where the design is yours.
  • The guide is one way to build it. The requirements are the contract; the path is negotiable.

How this works

  1. On Canvas, open Lab 1: Paddle Game and click the acceptance link (same Classroom50 flow as Lab 0). Clone your repo.
  2. Open project.godot in Godot; open GUIDE.md next to it.
  3. Build milestone by milestone. Commit and push at every milestone - pushing to main is submitting, and a half-built pushed game beats a finished unpushed one.

Requirements, short version: paddle (clamped), ball (bounces, serves after a miss), a visible rally/score, one custom feature, class code style (typed vars, @export tunables, * delta). Full list in the repo README, along with the Your Submission section you fill before the deadline.

The milestones

Builds You already met it as
M1 A paddle that obeys Blitz’s movement, one axis
M2 A berry that flies and bounces velocity * delta, viewport bounds
M3 The rally (paddle hits ball) The marionberry’s area_entered doorbell
M4 Speed-up, best rally, tuning @export dials, compounding difficulty
M5 Make it yours (required) Your imagination, plus the menu in the guide

M5’s menu: second player (with your first custom Input Map actions), angle control off the paddle face, lives and game over, an on-screen score Label, juice (time_scale slow-mo on a miss), or a full reskin. Or invent your own.

Cabinet open

Build until 11:00. I am circulating - flag me down early and often.

Stuck? In order: the Nothing happened? table at the bottom of GUIDE.md → ask “which class moment is this?” → your neighbor → me.

At 11:00 sharp: status check - everyone names their M5 twist out loud. Claim it before someone else does.

Game Over Screen

Quest log

Lab 0: Press Start - due tonight, 9:59 PM via Canvas. Last call.

Lab 1: Paddle Game - due Thursday, September 10, 9:59 PM. Keep building from where class left off; the guide walks the whole way.

Reading for Thursday (highly recommended): Godot: Your first 2D game - skim the player scene sections; you will recognize almost everything now.

Next time: input actions done right, real movement with velocity + move_and_slide() on the physics clock, and collision layers and masks - the ghost learns manners, and the rival cast starts becoming real.

References

Sources

  1. Nystrom, R. Game Programming Patterns: Game Loop and Update Method.
  2. Godot Engine documentation, Idle and Physics Processing: docs.godotengine.org.
  3. Class repos: pac-blitz (reference) and the Lab 1 starter (via your Classroom50 link).