Video Game Design and Coding

Week 29 packet

Student instructions, tasks, and checks. The answer key is not in this packet. Links stay as text. This page does not embed a clip or run a file.

# Week 29 — Final capstone design

## VGC-L141 — Choose a final game worth finishing

**Objective:** Write an original design with a realistic audience and scope.

**Time:** 55 minutes

### Instruction
Your final capstone is a small original 2D game using the same Godot/GDScript pathway. Choose a collector, arcade game, narrative puzzle, platformer or a restrained combination. Target five to ten minutes of play or a short replayable loop. Require input, rules, visible state, a challenge, a clear ending and restart. Include one richer system from the second half: validated local saves, readable enemy states or constrained procedural content. No online multiplayer, public leaderboard or paid dependency is required.

### Worked example
A capstone risk experiment can test one readable chase/escape interaction before a designer builds several rooms around that mechanic.

### Assignment
1. Write a one-page pitch naming genre, audience, core loop and richer system.
2. Explain two original design choices and list reused personal scaffolds.

### Acceptance checks
- The proposal describes a complete achievable game.
- Originality is more than changing names or colors.

**Submit:** Final capstone proposal and reuse declaration.

**Question:** Why include only one richer system as a minimum?

**Support:** Choose one familiar 2D genre, a short complete loop and no networking. Reuse only code you can explain. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Propose one optional feature with an explicit dependency and a stop condition. Document one changed parameter, your prediction, and the observed effect; optional, not required for completion.

**Optional free reading:** [Your first 2D game](https://docs.godotengine.org/en/stable/getting_started/first_2d_game/index.html)

---

## VGC-L142 — Write a final testable feature contract

**Objective:** Define what must work before release.

**Time:** 55 minutes

### Instruction
Create ten required acceptance tests: launch, input, core rule, challenge, progress feedback, ending, replay, richer system, comfort/accessibility and source restoration. Each needs setup, action and expected result. Add two adversarial cases such as repeated rewards, insufficient resources, invalid save data or a boundary AI distance. Keep optional features in a separate list. These twelve tests establish the final minimum; changing them later requires a documented design revision rather than silently lowering expectations after a failure.

### Worked example
A capstone risk experiment can test one readable chase/escape interaction before a designer builds several rooms around that mechanic.

### Assignment
1. Write ten required and two adversarial tests.
2. Assign evidence type and priority to each.

### Acceptance checks
- All twelve specify observable outcomes.
- The richer-system test covers a failure path as well as ordinary success.

**Submit:** Final twelve-test contract.

**Question:** Why distinguish a required test from an optional feature?

**Support:** Choose one familiar 2D genre, a short complete loop and no networking. Reuse only code you can explain. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Propose one optional feature with an explicit dependency and a stop condition. Document one changed parameter, your prediction, and the observed effect; optional, not required for completion.

**Optional free reading:** [Your first 2D game](https://docs.godotengine.org/en/stable/getting_started/first_2d_game/index.html)

---

## VGC-L143 — Design the data and node architecture

**Objective:** Plan state ownership, dependencies and safe persistence if used.

**Time:** 55 minutes

### Instruction
Draw the final node tree and name the owner of phase, score/progress, player motion, challenge state and settings. Map events between systems. If using saves, write the schema and validation policy; if using AI, write its state transitions; if using generation, write its invariants. Avoid storing the same authoritative value in multiple scripts. List exact scene/script filenames so the project remains inspectable. Architecture should support your proposed game, not imitate a complex commercial framework you do not need.

### Worked example
A capstone risk experiment can test one readable chase/escape interaction before a designer builds several rooms around that mechanic.

### Assignment
1. Create node, state-owner and event-flow diagrams.
2. Add a schema, state machine or invariant table for the richer system.

### Acceptance checks
- Every required state has one owner.
- The richer system has explicit validity rules.

**Submit:** Final technical design packet.

**Question:** What should happen if a HUD value disagrees with the manager?

**Support:** Choose one familiar 2D genre, a short complete loop and no networking. Reuse only code you can explain. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Propose one optional feature with an explicit dependency and a stop condition. Document one changed parameter, your prediction, and the observed effect; optional, not required for completion.

**Optional free reading:** [Your first 2D game](https://docs.godotengine.org/en/stable/getting_started/first_2d_game/index.html)

---

## VGC-L144 — Identify risks and prototype the hardest part

**Objective:** Test uncertainty before building large amounts of content.

**Time:** 55 minutes

### Instruction
A risk is something that could prevent completion: an unfamiliar save policy, a difficult camera layout, a procedural room that blocks a route or a UI that cannot fit required text. Rank risks by impact and uncertainty. Build a tiny throwaway experiment for the highest one using the existing recipes. Write a pass condition before testing. If it fails, simplify the design now. A risk prototype is useful even if none of its code enters the final project.

### Worked example
A capstone risk experiment can test one readable chase/escape interaction before a designer builds several rooms around that mechanic.

Scaffold C-TESTS — A tiny pure-function test harness
Setup: Attach to Node TestLab. If using command line, create a separate SceneTree harness rather than running this Node script with --script. Run current TestLab scene in editor; assert reports a failed condition in Debugger. No external test framework or package is needed.
```gdscript
extends Node
func bounded_score(old_score: int, change: int) -> int:
    return clampi(old_score + change, 0, 999)
func _ready() -> void:
    assert(bounded_score(10, 2) == 12)
    assert(bounded_score(0, -3) == 0)
    assert(bounded_score(998, 9) == 999)
    print("3 score checks passed")
```

Scaffold C-STATE-SAVE — Validated inventory and checkpoint data
Setup: Attach to a Node named SaveData. This is a separate file from C-SAVE and only reads fixed known scalar fields. get_checkpoint_data returns plain data, not live node references. When applying a loaded checkpoint, the assignment maps checkpoint ID to a teacher-approved safe Marker2D, never to a filename supplied by the save.
```gdscript
extends Node
const PATH: String = "user://seed_station_v1.json"
func defaults() -> Dictionary:
    return {"version": 1, "seed": 0, "water": 0, "checkpoint": "start"}
func _bounded_count(value: Variant) -> int:
    if value is int or value is float:
        return clampi(int(value), 0, 99)
    return 0
func validate(raw: Variant) -> Dictionary:
    var clean := defaults()
    if not raw is Dictionary:
        return clean
    var data: Dictionary = raw
    if data.get("version", 0) != 1:
        return clean
    clean["seed"] = _bounded_count(data.get("seed", 0))
    clean["water"] = _bounded_count(data.get("water", 0))
    var checkpoint: Variant = data.get("checkpoint", "start")
    if checkpoint is String and checkpoint in ["start", "greenhouse"]:
        clean["checkpoint"] = checkpoint
    return clean
func save_data(raw: Dictionary) -> bool:
    var file := FileAccess.open(PATH, FileAccess.WRITE)
    if file == null:
        return false
    file.store_string(JSON.stringify(validate(raw)))
    file.close()
    return true
func load_data() -> Dictionary:
    if not FileAccess.file_exists(PATH):
        return defaults()
    var file := FileAccess.open(PATH, FileAccess.READ)
    if file == null:
        return defaults()
    var text: String = file.get_as_text()
    file.close()
    var parser := JSON.new()
    if parser.parse(text) != OK:
        return defaults()
    return validate(parser.data)
```

Scaffold C-GUARD-AI — A readable patrol/chase/return state machine
Setup: Create CharacterBody2D Guard with visible circle or polygon and matching collider. Layer 4 mask 2 for walls. Attach script and assign target to your Player instance in Inspector. Level is one open rectangle; this is steering, not navigation around walls. Damage remains a separate sensor/manager event. Draw detection radii on a design diagram before tuning values.
```gdscript
extends CharacterBody2D
enum Mode { PATROL, CHASE, RETURN }
@export var target: Node2D
@export var speed: float = 70.0
var mode: Mode = Mode.PATROL
var home: Vector2
var patrol_target: Vector2
func _ready() -> void:
    home = global_position
    patrol_target = home + Vector2(80, 0)
func _physics_process(delta: float) -> void:
    if not is_instance_valid(target):
        velocity = Vector2.ZERO
        return
    var distance: float = global_position.distance_to(target.global_position)
    if mode != Mode.CHASE and distance < 120.0:
        mode = Mode.CHASE
    elif mode == Mode.CHASE and distance > 180.0:
        mode = Mode.RETURN
    var destination: Vector2
    if mode == Mode.CHASE:
        destination = target.global_position
    elif mode == Mode.RETURN:
        destination = home
        if global_position.distance_to(home) <= maxf(3.0, speed * delta):
            mode = Mode.PATROL
    else:
        destination = patrol_target
        if global_position.distance_to(patrol_target) <= maxf(3.0, speed * delta):
            patrol_target = home if patrol_target != home else home + Vector2(80, 0)
    var offset := destination - global_position
    velocity = offset.normalized() * minf(speed, offset.length() / maxf(delta, 0.001))
    move_and_slide()
```

Scaffold C-GRID — A deterministic grid with a guaranteed clear route
Setup: Attach to Node2D GridLab. This example draws, not physics, so the assignment adds matching wall collision only after validation. Grid is 8 columns by 6 rows at 40 pixels per cell. Row 0 and column 7 are a guaranteed clear route from (0,0) to (7,5). RNG seed is a nonpersonal integer.
```gdscript
extends Node2D
const COLS: int = 8
const ROWS: int = 6
const CELL: int = 40
var cells: Array = []
func _ready() -> void:
    var rng := RandomNumberGenerator.new()
    rng.seed = 2468
    for y in range(ROWS):
        var row: Array[int] = []
        for x in range(COLS):
            var blocked: bool = y != 0 and x != COLS - 1 and rng.randf() < 0.25
            row.append(1 if blocked else 0)
        cells.append(row)
    queue_redraw()
func _draw() -> void:
    for y in range(cells.size()):
        for x in range(cells[y].size()):
            var color := Color.DIM_GRAY if cells[y][x] == 1 else Color.BEIGE
            draw_rect(Rect2(x * CELL, y * CELL, CELL - 2, CELL - 2), color)
```

### Assignment
1. Rank three risks and choose the highest.
2. Build and test a minimal experiment with a written pass condition.

### Acceptance checks
- The experiment isolates one uncertainty.
- The result leads to a keep, simplify or replace decision.

**Submit:** Risk table, experiment source and decision note.

**Question:** Why prototype the uncertain system before drawing twenty levels?

**Support:** Choose one familiar 2D genre, a short complete loop and no networking. Reuse only code you can explain. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Propose one optional feature with an explicit dependency and a stop condition. Document one changed parameter, your prediction, and the observed effect; optional, not required for completion.

**Optional free reading:** [Your first 2D game](https://docs.godotengine.org/en/stable/getting_started/first_2d_game/index.html)

---

## VGC-L145 — Plan work in dependency order

**Objective:** Break a capstone into small demonstrable tasks.

**Time:** 55 minutes

### Instruction
A useful task is small enough to verify in one lesson and has a visible result. “Build game” is too broad; “connect the third pickup and prove score becomes three” is testable. Arrange tasks so foundations precede dependent features: input before challenge testing, state before endings, valid data before save loading. Schedule a feature freeze and time for testing. This course is parent-paced; these are work stages, not automatic calendar deadlines or enrollments.

### Worked example
A capstone risk experiment can test one readable chase/escape interaction before a designer builds several rooms around that mechanic.

### Assignment
1. Create a must-have task board with at least twelve small tasks.
2. Mark dependencies, a feature-freeze stage and a testing buffer.

### Acceptance checks
- Each task has a completion test.
- No task depends on an unfinished later foundation.

**Submit:** Capstone task board and dependency map.

**Question:** Why reserve time for testing before adding optional content?

**Support:** Choose one familiar 2D genre, a short complete loop and no networking. Reuse only code you can explain. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Propose one optional feature with an explicit dependency and a stop condition. Document one changed parameter, your prediction, and the observed effect; optional, not required for completion.

**Optional free reading:** [Your first 2D game](https://docs.godotengine.org/en/stable/getting_started/first_2d_game/index.html)

---