CS 276 // GAME SYSTEMS IN GODOT

Split Screen.

Lecture W6R1: Carving Code, Git for Two, Lab 4

Lucas P. Cordova, Ph.D.

Willamette University  |  Thursday, October 1, 2026

SCORE 000276HI 999999

Outline

QUEST LOG

Lab 3 is due tonight, 9:59 PM. The proposal is due Thursday, October 8, 9:59 PM. Lab 4 goes out today and is due Tuesday, October 20, 9:59 PM.

CS 276
LEVEL 01

One Reason to Change

Tuesday's parts get a name and a test

SCORE 000276HI 999999

Tuesday, in four rules

RULES OF PARTS
  • One job per part. If you cannot name the job in three words, split it.
  • The part owns its data. Health lives in HealthComponent, nowhere else.
  • Signals up, calls down. Parts announce; owners decide.
  • Numbers live in data. @export for one entity, a Resource file for shared tuning.

Rule one carries the most weight, and software engineering has a formal name for it.

CS 276

The Single Responsibility Principle

DEFINITION

Gather the things that change for the same reason. Separate the things that change for different reasons.

A module that follows the principle has one reason to change, and it answers to one kind of person asking for that change.

Robert C. Martin's formulation [1]. It is the S in SOLID, and it is the rule that tells you where to cut a god script into parts.

PATTERN

Component is the mechanism (a part is a child node). Single Responsibility is the judgment (which lines belong in which part).

CS 276

Three tests for one job

TestAskFails when
Three wordsCan you name the job in three words or fewer?The name needs an "and".
Who asksWho would request a change to this code?Designer, artist, and sound all would.
Lift outCould this code move to another entity untouched?It reaches into its owner or its neighbors.
CHECKPOINT

"Tracks hit points" passes all three. "Chases the player, flashes red, and updates the score" fails the first one out loud.

CS 276

Meet the Berry Bandit

An enemy from a Blitz side game. It chases Blitz, hurts them on contact, takes three marionberries to defeat, and sometimes drops a fig. It works. It is also one file doing eight jobs.

 1  # berry_bandit.gd (the whole enemy, one file)
 2  extends CharacterBody2D
 3
 4  var hp: int = 3
 5
 6  func _physics_process(_delta: float) -> void:
 7      var player = get_tree().get_first_node_in_group("player")
 8      var to_player: Vector2 = player.global_position - global_position
 9      if to_player.length() < 300.0:
10          velocity = to_player.normalized() * 180.0
11      else:
12          velocity = Vector2.ZERO
13      move_and_slide()
14      if to_player.length() < 24.0:
15          player.hp -= 1
16          player.get_node("Sprite2D").modulate = Color.RED
17          if player.hp <= 0:
18              get_tree().reload_current_scene()
CS 276

The Berry Bandit, continued

20  func hit_by_berry() -> void:
21      hp -= 1
22      $Sprite2D.modulate = Color.RED
23      $HurtSound.pitch_scale = randf_range(0.9, 1.1)
24      $HurtSound.play()
25      await get_tree().create_timer(0.15).timeout
26      $Sprite2D.modulate = Color.WHITE
27      if hp <= 0:
28          Game.score += 50
29          get_node("/root/Main/HUD/ScoreLabel").text = "Score: " + str(Game.score)
30          if randf() < 0.25:
31              var fig := preload("res://fig.tscn").instantiate()
32              fig.global_position = global_position
33              get_parent().add_child(fig)
34          queue_free()
WATCH OUT

Count the reasons this file could change: the chase feels unfair, the flash is too long, the sound designer has a new clip, the HUD moves, figs drop too often. Five reasons, one file.

CS 276

Boss fight: carve the bandit

CO-OP MODE

12 minutes, in pairs, on the handout:

  1. Tag every line with its job in the margin (chase, health, flash, ...).
  2. Group the jobs into parts. Name each part in three words or fewer.
  3. Draw the new scene tree: the bandit, its parts, and what stays in the brain.
  4. Wire it: one call down or one signal up for each part.
  5. Fill the tuning sheet: every magic number gets a name and moves into BanditStats.
  6. Fast pair? Bugs are hiding in this file. Find two, and say which part would have prevented each.
CS 276

Trade and defend

CO-OP MODE

4 minutes, two pairs together:

  1. Swap sheets. Find one border the other pair drew differently.
  2. Each side defends its cut with one of the three tests.
  3. Agree, or agree on what you would need to know to decide.

Designs differ. A cut you can defend with a test is a good cut.

CS 276
LEVEL 02

Git for Two

One game, two keyboards, zero collisions

SCORE 000276HI 999999

Small parts merge clean

  • Git merges by file, line by line. Two people editing different files never collide.
  • A 900-line player.gd guarantees you and your teammate are in the same file all week.
  • Parts change that: you take dash_component.gd, your teammate takes health_bar.gd.
  • The same is true for scenes. A small scene that is instanced into the main scene is a file one person can own.
PATTERN

Single Responsibility is also a teamwork tool: one job per file means one owner per change.

CS 276

What goes in the repo

FileCommit it?Why
*.gd, *.tscn, *.tresYesYour game. All three are plain text, so git can diff them.
project.godotYesInput map, layer names, main scene.
*.import, *.uidYesImport settings and stable IDs. Without them your teammate's scenes lose their references.
art, audioYesKeep sources small. Binary files cannot merge, so one owner per asset.
.godot/NeverThe editor's cache, rebuilt on every machine. The lab .gitignore already excludes it.

Starting your team repo from a lab template gives you the correct .gitignore for free [2].

CS 276

Scenes are text

[node name="Rival" type="CharacterBody2D"]
collision_layer = 4
script = ExtResource("1_rival")

[node name="HealthComponent" type="Node" parent="."]
script = ExtResource("2_health")
max_health = 3
  • A .tscn is a list of nodes and their changed properties.
  • Git can merge two edits to it, as long as they touch different nodes.
  • Two people rearranging the same scene on the same day produce conflicts nobody enjoys reading.
WATCH OUT

One editor per scene at a time. Say it in your team chat: "I have main.tscn until tonight." Split big scenes into small instanced ones so there is more to go around.

CS 276

The feature-branch loop

git switch main
git pull                         # start from the latest main
git switch -c dash-component     # one branch per feature

# ...build, run, commit small...
git add .
git commit -m "Add dash component with cooldown"
git push -u origin dash-component

# on GitHub: open a pull request, teammate reviews, merge
git switch main
git pull                         # both of you, after every merge
  • main always runs. Stakeholders clone main at every gate.
  • A branch lives a day or two. Long branches drift.
  • The pull request is where your teammate reads your code before it lands.

After pulling a change to project.godot: Project menu, then Reload Current Project.

CS 276

When a conflict hits

Conflict inWhat to do
A script (.gd)Open the file, read both versions between the markers, keep what is right, delete the markers, run the game, commit.
A scene or resource (.tscn, .tres)Pick one side whole (git checkout --theirs path or --ours), then redo the smaller change in the editor.
A binary (art, audio)Pick one side whole. Ask the owner which one.
CHECKPOINT

Conflicts are a scheduling problem more than a git problem. Pull before you start, commit small, merge daily, and most weeks you will never see one.

CS 276

Co-op: the team git pact

CO-OP MODE

4 minutes, with your teammate, straight into your proposal's branching plan:

  1. Branch names: pick a pattern (feature/dash, fix/spawn).
  2. Who merges: every pull request gets the other teammate's eyes first.
  3. Scene owners this sprint: list your first three scenes and put a name on each.
  4. Sync habit: when do you both pull? (Start of every work session is a fine answer.)
CS 276
LEVEL 03

Lab 4: Component Kit

Build the parts once, bolt them onto two games

SCORE 000276HI 999999

Lab 4: the Component Kit

One Godot project, two tiny games (the lab calls them cabinets), and one shared folder of parts. The Berry Bandit you carved on paper is the first thing you build.

THE SHAPE OF THE LAB
lab4-component-kit/ ├─ components/ the kit: one copy of every part ├─ cabinet_a/ Bandit Brawl (Blitz, berries, bandits) ├─ cabinet_b/ a second game, different entities └─ stats/ Resource files: the tuning sheets
WATCH OUT

The kit rule: both cabinets use the same files in components/. A part that needs editing to work in the second cabinet has two jobs.

Solo lab. Due Tuesday, October 20, 9:59 PM.

CS 276

Milestones

You buildThe idea
M0Accept, clone, run both cabinets. Things move; nothing can be hurt.Starting line
M1HealthComponent, bolted onto Blitz and the bandit.A part is a child node
M2HitboxComponent and HurtboxComponent: damage travels between parts.Signals up, calls down
M3The second cabinet, using the kit with zero edits in components/.Reuse
M4A stats Resource and at least three .tres files. Magic numbers leave the code.Numbers live in data
M5Your own part, used by two different entities.One job, yours

M5 ideas: flash on hit, drop on death, knockback, a health bar, a lifetime timer. GUIDE.md has the full menu.

CS 276

How it is graded

CriterionPoints
The kit works in cabinet A: health, hitbox, hurtbox, defeat25
Reuse: cabinet B runs on the same, unedited components/ files20
Rules of parts: one job each, parts own their data, signals up and calls down, no reaching into owners15
Numbers in data: a stats Resource, at least three .tres files, no magic numbers in scripts15
Your own part (M5), used by two different entities10
Your Submission in the README: parts list, wiring sketch, tuning sheet, reflection15

Your handout from today is a first draft of that README section, for a different enemy.

CS 276

Studio time

STUDIO

Until class ends, pick your lane:

  • Lab 3 still open? That comes first. It is due tonight, 9:59 PM.
  • Lab 3 pushed? Accept Lab 4, run both cabinets, and start M1 with your handout beside you.
  • Team time: set up the git pact in your team repo and make your first feature branch.
QUEST LOG

Tonight: Lab 3, 9:59 PM. Thursday, October 8: proposal (PDF in Canvas and in your repo), 9:59 PM. Tuesday, October 20: Lab 4, 9:59 PM. Next week: UI systems on Tuesday, agile for game teams on Thursday.

CS 276
LEVEL 04

Game Over Screen

What you know now

SCORE 000276HI 999999

Checkpoint reached

CHECKPOINT
  • Single Responsibility: gather what changes for the same reason, separate what changes for different reasons.
  • Three tests find the cut: three words, who asks, lift out.
  • Bugs cluster where a script reaches into someone else's job.
  • Small files and small scenes are what let two people work on one game. Branch per feature, pull often, main always runs.
CS 276

Sources

  • [1] Robert C. Martin, The Single Responsibility Principle (blog.cleancoder.com, 2014), and Clean Architecture, chapter 7.
  • [2] Godot docs: Version control systems (docs.godotengine.org, Best Practices section), today's reading.
  • [3] Nystrom, Game Programming Patterns: Component (gameprogrammingpatterns.com/component.html).
  • [4] The 20 Games Challenge (20gameschallenge.com), the anchor list for your pitches.
CS 276