Video Game Design and Coding

Week 6 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 06 — Improve a complete game

## VGC-L026 — Refactor repeated feedback

**Objective:** Extract a function while keeping observable behavior unchanged.

**Time:** 55 minutes

### Instruction
Refactoring changes internal organization without changing the game’s observable rules. C-MANAGER uses _refresh so score formatting and completion text are written in one place. Find another repeated operation, such as a debug status message, and put it into a named function. Call it from the same events that previously contained the repeated lines. Do not add a new mechanic in the same edit. Run the existing zero-to-three and restart tests to prove the reorganization did not break the game.

### Worked example
Two parameter trials can use the same start and route while changing only speed. Record an observation such as two wall contacts rather than an unsupported claim such as more fun.

Scaffold C-MANAGER — A complete three-coin collection round
Setup: Create World Node2D root with Player (C-BODY), three Coin instances (C-PICKUP), and CanvasLayer HUD > Label named Score plus Button named Restart (text Restart, position 500,10). Root and parents unscaled. Name coins Coin1, Coin2, Coin3. Place them at (250,100),(380,220),(520,100). Player group player. Attach script to World. UI paths and coin names must match exactly. Connect nothing else in the editor. Label at (10,10).
```gdscript
extends Node2D
var score: int = 0
var playing: bool = true
@onready var score_label: Label = $HUD/Score
func _ready() -> void:
    $HUD/Restart.pressed.connect(_restart)
    for coin in [$Coin1, $Coin2, $Coin3]:
        coin.collected.connect(_on_collected)
    _refresh()
func _on_collected(points: int) -> void:
    if not playing:
        return
    score += points
    if score >= 3:
        playing = false
        $Player.set_physics_process(false)
    _refresh()
func _refresh() -> void:
    score_label.text = "Score: %d / 3" % score
    if not playing:
        score_label.text += " — Orchard complete!"
func _restart() -> void:
    get_tree().reload_current_scene()
```

### Assignment
1. Extract one genuinely repeated operation into a function.
2. List its callers and run both round tests.

### Acceptance checks
- Before/after behavior is identical.
- The repeated logic has one definition rather than copied blocks.

**Submit:** Refactored project and a caller diagram.

**Question:** How is refactoring different from adding a fourth coin?

**Support:** Keep the three-coin version working. Duplicate the project folder before experimenting with one parameter at a time. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Make one alternate layout with the same rules and compare route clarity. Document one changed parameter, your prediction, and the observed effect; optional, not required for completion.

**Optional free reading:** [Godot step by step: nodes, scenes, GDScript, input, signals](https://docs.godotengine.org/en/stable/getting_started/step_by_step/index.html)

---

## VGC-L027 — Balance speed with a controlled experiment

**Objective:** Measure a parameter change using a comparable test route.

**Time:** 55 minutes

### Instruction
A parameter is a value you can tune without rewriting the algorithm. C-BODY exposes speed in the Inspector. Compare speeds 120, 160 and 200 on the same route, starting from the same position. Record approximate completion time and wall collisions for two runs per speed. More speed is not automatically better: a tight route may become hard to steer. Your recommendation should refer to evidence and the intended beginner player, not simply the fastest result.

### Worked example
Two parameter trials can use the same start and route while changing only speed. Record an observation such as two wall contacts rather than an unsupported claim such as more fun.

Scaffold C-BODY — Reusable top-down player with collision
Setup: Create CharacterBody2D root Player at (80,180); add CollisionShape2D with CircleShape2D radius 14. Attach this script to Player and save Player.tscn. Use C-FREE-MOVE input actions. Root collision layer 1, mask 2. Create a separate StaticBody2D Wall on layer 2 mask 1 with RectangleShape2D collision and a Polygon2D rectangle as visible child. Instance both under an unscaled Node2D World. Do not put a body inside another physics body.
```gdscript
extends CharacterBody2D
@export var speed: float = 160.0
func _draw() -> void:
    draw_circle(Vector2.ZERO, 14.0, Color.CORNFLOWER_BLUE)
    draw_line(Vector2(-8, 0), Vector2(8, 0), Color.WHITE, 3.0)
func _physics_process(_delta: float) -> void:
    velocity = Input.get_vector("move_left", "move_right", "move_up", "move_down") * speed
    move_and_slide()
```

### Assignment
1. Run the same route twice at each of three speeds.
2. Choose one speed and justify it with time and steering observations.

### Acceptance checks
- Six runs use the same layout and start.
- Recommendation cites at least two observations.

**Submit:** Speed experiment table and chosen-speed project.

**Question:** What must stay constant for a useful speed comparison?

**Support:** Keep the three-coin version working. Duplicate the project folder before experimenting with one parameter at a time. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Make one alternate layout with the same rules and compare route clarity. Document one changed parameter, your prediction, and the observed effect; optional, not required for completion.

**Optional free reading:** [Godot step by step: nodes, scenes, GDScript, input, signals](https://docs.godotengine.org/en/stable/getting_started/step_by_step/index.html)

---

## VGC-L028 — Create an original optional route

**Objective:** Offer a meaningful choice without punishing a beginner unfairly.

**Time:** 55 minutes

### Instruction
A meaningful route choice trades one advantage for another. For Pocket Orchard, one path can be longer and wide, while a shortcut is shorter but narrow enough to require care. Both must remain physically passable. Put a distinctive symbol near the optional shortcut so it does not depend on color recognition. The win condition stays collecting the same three coins. A choice is not meaningful if one path is faster, safer and easier in every situation.

### Worked example
Two parameter trials can use the same start and route while changing only speed. Record an observation such as two wall contacts rather than an unsupported claim such as more fun.

Scaffold C-BODY — Reusable top-down player with collision
Setup: Create CharacterBody2D root Player at (80,180); add CollisionShape2D with CircleShape2D radius 14. Attach this script to Player and save Player.tscn. Use C-FREE-MOVE input actions. Root collision layer 1, mask 2. Create a separate StaticBody2D Wall on layer 2 mask 1 with RectangleShape2D collision and a Polygon2D rectangle as visible child. Instance both under an unscaled Node2D World. Do not put a body inside another physics body.
```gdscript
extends CharacterBody2D
@export var speed: float = 160.0
func _draw() -> void:
    draw_circle(Vector2.ZERO, 14.0, Color.CORNFLOWER_BLUE)
    draw_line(Vector2(-8, 0), Vector2(8, 0), Color.WHITE, 3.0)
func _physics_process(_delta: float) -> void:
    velocity = Input.get_vector("move_left", "move_right", "move_up", "move_down") * speed
    move_and_slide()
```

Scaffold C-MANAGER — A complete three-coin collection round
Setup: Create World Node2D root with Player (C-BODY), three Coin instances (C-PICKUP), and CanvasLayer HUD > Label named Score plus Button named Restart (text Restart, position 500,10). Root and parents unscaled. Name coins Coin1, Coin2, Coin3. Place them at (250,100),(380,220),(520,100). Player group player. Attach script to World. UI paths and coin names must match exactly. Connect nothing else in the editor. Label at (10,10).
```gdscript
extends Node2D
var score: int = 0
var playing: bool = true
@onready var score_label: Label = $HUD/Score
func _ready() -> void:
    $HUD/Restart.pressed.connect(_restart)
    for coin in [$Coin1, $Coin2, $Coin3]:
        coin.collected.connect(_on_collected)
    _refresh()
func _on_collected(points: int) -> void:
    if not playing:
        return
    score += points
    if score >= 3:
        playing = false
        $Player.set_physics_process(false)
    _refresh()
func _refresh() -> void:
    score_label.text = "Score: %d / 3" % score
    if not playing:
        score_label.text += " — Orchard complete!"
func _restart() -> void:
    get_tree().reload_current_scene()
```

### Assignment
1. Build a wide route and an optional shortcut to one coin.
2. Mark the shortcut with an original shape symbol and compare both routes.

### Acceptance checks
- Both routes reach the same coin.
- The shortcut offers a tradeoff rather than an invisible obstruction.

**Submit:** Revised map and a two-route comparison note.

**Question:** When is one route strictly dominant?

**Support:** Keep the three-coin version working. Duplicate the project folder before experimenting with one parameter at a time. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Make one alternate layout with the same rules and compare route clarity. Document one changed parameter, your prediction, and the observed effect; optional, not required for completion.

**Optional free reading:** [Godot step by step: nodes, scenes, GDScript, input, signals](https://docs.godotengine.org/en/stable/getting_started/step_by_step/index.html)

---

## VGC-L029 — Observe a private playtest

**Objective:** Collect anonymous observations without coaching every action.

**Time:** 55 minutes

### Instruction
Ask a willing family member to try the game locally; if nobody is available, replay after a break using the written instructions only. Explain what will be observed and let them stop. Do not record a face, full name or personal information. Watch where they hesitate, miss feedback or misunderstand controls. Avoid directing their hands; a confused action is useful evidence about the interface. Separate observations from interpretations: “pressed Space three times” is an observation; “wanted to jump” is an interpretation.

### Worked example
Two parameter trials can use the same start and route while changing only speed. Record an observation such as two wall contacts rather than an unsupported claim such as more fun.

### Assignment
1. Conduct a five-minute local test or structured self-test.
2. Record three observations and rank two improvements by impact.

### Acceptance checks
- Notes contain no identifying player data.
- Each proposed improvement links to an observed problem.

**Submit:** Anonymous playtest sheet and prioritized issue list.

**Question:** Why avoid explaining every confusing part during the test?

**Support:** Keep the three-coin version working. Duplicate the project folder before experimenting with one parameter at a time. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Make one alternate layout with the same rules and compare route clarity. Document one changed parameter, your prediction, and the observed effect; optional, not required for completion.

**Optional free reading:** [Godot step by step: nodes, scenes, GDScript, input, signals](https://docs.godotengine.org/en/stable/getting_started/step_by_step/index.html)

---

## VGC-L030 — Submit Pocket Orchard with evidence

**Objective:** Package editable code and demonstrate that requirements still work.

**Time:** 55 minutes

### Instruction
A playable video does not prove which code you wrote or allow a teacher to inspect a bug. Submit a complete source project, a short readme, and a test log. Exclude the regenerable .godot cache from the ZIP, but keep project.godot, scripts, scenes and original assets. Restore the ZIP to a new folder and run the main scene before submitting. A private HTTPS download link is acceptable when the assignment page cannot yet attach ZIP files. The teacher should download files, not execute an arbitrary web script.

### Worked example
Two parameter trials can use the same start and route while changing only speed. Record an observation such as two wall contacts rather than an unsupported claim such as more fun.

Scaffold C-BODY — Reusable top-down player with collision
Setup: Create CharacterBody2D root Player at (80,180); add CollisionShape2D with CircleShape2D radius 14. Attach this script to Player and save Player.tscn. Use C-FREE-MOVE input actions. Root collision layer 1, mask 2. Create a separate StaticBody2D Wall on layer 2 mask 1 with RectangleShape2D collision and a Polygon2D rectangle as visible child. Instance both under an unscaled Node2D World. Do not put a body inside another physics body.
```gdscript
extends CharacterBody2D
@export var speed: float = 160.0
func _draw() -> void:
    draw_circle(Vector2.ZERO, 14.0, Color.CORNFLOWER_BLUE)
    draw_line(Vector2(-8, 0), Vector2(8, 0), Color.WHITE, 3.0)
func _physics_process(_delta: float) -> void:
    velocity = Input.get_vector("move_left", "move_right", "move_up", "move_down") * speed
    move_and_slide()
```

Scaffold C-PICKUP — An exactly-once pickup signal
Setup: Create Area2D root Coin, CollisionShape2D Circle radius 10. Layer 3 (bit value 4), mask layer 1 only. Monitoring on. Attach script and save Coin.tscn. In the Node > Groups editor add Player root to group player. Put a Coin instance and Player instance in World. Manager connects each Coin.collected to its score handler (see C-MANAGER). Signal wiring here is code-only: do not also connect body_entered in editor.
```gdscript
extends Area2D
signal collected(points: int)
@export var points: int = 1
var taken: bool = false
func _ready() -> void:
    body_entered.connect(_on_body_entered)
func _draw() -> void:
    draw_circle(Vector2.ZERO, 10.0, Color.GOLD)
    draw_line(Vector2(-5, 0), Vector2(5, 0), Color.BLACK, 2.0)
func _on_body_entered(body: Node2D) -> void:
    if taken or not body.is_in_group("player"):
        return
    taken = true
    collected.emit(points)
    queue_free()
```

Scaffold C-MANAGER — A complete three-coin collection round
Setup: Create World Node2D root with Player (C-BODY), three Coin instances (C-PICKUP), and CanvasLayer HUD > Label named Score plus Button named Restart (text Restart, position 500,10). Root and parents unscaled. Name coins Coin1, Coin2, Coin3. Place them at (250,100),(380,220),(520,100). Player group player. Attach script to World. UI paths and coin names must match exactly. Connect nothing else in the editor. Label at (10,10).
```gdscript
extends Node2D
var score: int = 0
var playing: bool = true
@onready var score_label: Label = $HUD/Score
func _ready() -> void:
    $HUD/Restart.pressed.connect(_restart)
    for coin in [$Coin1, $Coin2, $Coin3]:
        coin.collected.connect(_on_collected)
    _refresh()
func _on_collected(points: int) -> void:
    if not playing:
        return
    score += points
    if score >= 3:
        playing = false
        $Player.set_physics_process(false)
    _refresh()
func _refresh() -> void:
    score_label.text = "Score: %d / 3" % score
    if not playing:
        score_label.text += " — Orchard complete!"
func _restart() -> void:
    get_tree().reload_current_scene()
```

### Assignment
1. Package the original orchard game and readme with controls, goal and known limitations.
2. Re-run movement, walls, three pickups, completion and restart from the restored copy.

### Acceptance checks
- The restored copy opens without missing resources.
- All five feature checks have actual results, not only “done.”

**Submit:** Pocket Orchard source ZIP or private downloadable artifact link, readme, and five-check log; optional 60-second playthrough.

**Question:** What must accompany a playthrough for programming assessment?

**Support:** Keep the three-coin version working. Duplicate the project folder before experimenting with one parameter at a time. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Make one alternate layout with the same rules and compare route clarity. Document one changed parameter, your prediction, and the observed effect; optional, not required for completion.

**Optional free reading:** [Godot step by step: nodes, scenes, GDScript, input, signals](https://docs.godotengine.org/en/stable/getting_started/step_by_step/index.html)

---