Video Game Design and Coding

Week 15 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 15 — Platformer testing and explanation

## VGC-L071 — Measure a difficulty curve

**Objective:** Use attempts and recovery distance rather than intuition alone.

**Time:** 55 minutes

### Instruction
A difficulty curve describes how demands change through the level. Test each jump separately for five attempts, recording successes and the distance repeated after failure. A difficult jump near the start may be less frustrating than a moderate jump after a long unskippable section. Use your checkpoint intentionally to control repetition cost. Compare evidence from attempts with the teaching sequence: introduce, practice, combine. Do not assume every player’s results will match your own; report the limits of the sample.

### Worked example
For a repeated route test, keep controls and start position fixed. A table of attempts helps distinguish a consistently hard landing from a one-time input mistake.

Scaffold C-PLATFORM — A gravity-and-jump platform player
Setup: CharacterBody2D root Player, child CollisionShape2D RectangleShape2D size (24,32), no scale changes. Layer 1 mask 2. StaticBody2D floor layer 2 mask 1 with rectangular collider. Add Input Map move_left, move_right and jump bound to Space. Place player above floor, not intersecting it. Coordinates use positive y downward. Attach script to Player.
```gdscript
extends CharacterBody2D
@export var speed: float = 160.0
@export var gravity: float = 900.0
@export var jump_speed: float = 340.0
func _draw() -> void:
    draw_rect(Rect2(-12, -16, 24, 32), Color.SKY_BLUE)
    draw_line(Vector2(-8, -4), Vector2(8, -4), Color.BLACK, 2.0)
func _physics_process(delta: float) -> void:
    velocity.x = Input.get_axis("move_left", "move_right") * speed
    if not is_on_floor():
        velocity.y += gravity * delta
    if Input.is_action_just_pressed("jump") and is_on_floor():
        velocity.y = -jump_speed
    move_and_slide()
```

### Assignment
1. Record five attempts on each of three jumps.
2. Revise one platform or checkpoint based on the pattern.

### Acceptance checks
- The report contains fifteen attempts and actual outcomes.
- The revision targets a specific repeated problem.

**Submit:** Difficulty table and before/after layout.

**Question:** Why measure retry distance as well as success rate?

**Support:** Use the three-jump route and one checkpoint as the minimum game. Remove optional hazards until required tests pass. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Create a second optional route using the same movement code. 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-L072 — Diagnose a jump bug by category

**Objective:** Separate input, motion, collision and camera faults.

**Time:** 55 minutes

### Instruction
A failed jump can have different causes. If Space is not recognized, inspect Input Map. If vertical velocity is positive on jump, inspect the impulse sign. If the player catches a seam, inspect geometry. If the landing exists but cannot be seen, inspect camera framing. Use one known fault in a duplicate project, such as changing jump_speed’s sign, then document prediction, observation, diagnosis and fix. Read the first debugger error before testing visual behavior.

### Worked example
For a repeated route test, keep controls and start position fixed. A table of attempts helps distinguish a consistently hard landing from a one-time input mistake.

Scaffold C-PLATFORM — A gravity-and-jump platform player
Setup: CharacterBody2D root Player, child CollisionShape2D RectangleShape2D size (24,32), no scale changes. Layer 1 mask 2. StaticBody2D floor layer 2 mask 1 with rectangular collider. Add Input Map move_left, move_right and jump bound to Space. Place player above floor, not intersecting it. Coordinates use positive y downward. Attach script to Player.
```gdscript
extends CharacterBody2D
@export var speed: float = 160.0
@export var gravity: float = 900.0
@export var jump_speed: float = 340.0
func _draw() -> void:
    draw_rect(Rect2(-12, -16, 24, 32), Color.SKY_BLUE)
    draw_line(Vector2(-8, -4), Vector2(8, -4), Color.BLACK, 2.0)
func _physics_process(delta: float) -> void:
    velocity.x = Input.get_axis("move_left", "move_right") * speed
    if not is_on_floor():
        velocity.y += gravity * delta
    if Input.is_action_just_pressed("jump") and is_on_floor():
        velocity.y = -jump_speed
    move_and_slide()
```

### Assignment
1. Inject one reversible jump fault in a duplicate.
2. Write a category-based diagnosis and verify the same reproduction after fixing.

### Acceptance checks
- The report distinguishes cause from symptom.
- Original project remains intact and restored duplicate passes.

**Submit:** Bug report and repaired platform checkpoint.

**Question:** Would increasing jump speed fix an input action spelled incorrectly?

**Support:** Use the three-jump route and one checkpoint as the minimum game. Remove optional hazards until required tests pass. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Create a second optional route using the same movement code. 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-L073 — Explain the motion model without memorizing code

**Objective:** Trace one physics step in words and numbers.

**Time:** 55 minutes

### Instruction
Understanding code includes predicting a step. Suppose horizontal input is right, speed is 160, current y velocity is -100, gravity is 900 and delta is 0.02 while airborne. The next horizontal velocity is 160 and vertical velocity becomes -82 before move_and_slide. The character is still rising because y velocity remains negative. Write this trace in a table. Do not multiply the final velocity by delta before handing it to move_and_slide. Explain which operation actually resolves contact with floors and walls.

### Worked example
For a repeated route test, keep controls and start position fixed. A table of attempts helps distinguish a consistently hard landing from a one-time input mistake.

Scaffold C-PLATFORM — A gravity-and-jump platform player
Setup: CharacterBody2D root Player, child CollisionShape2D RectangleShape2D size (24,32), no scale changes. Layer 1 mask 2. StaticBody2D floor layer 2 mask 1 with rectangular collider. Add Input Map move_left, move_right and jump bound to Space. Place player above floor, not intersecting it. Coordinates use positive y downward. Attach script to Player.
```gdscript
extends CharacterBody2D
@export var speed: float = 160.0
@export var gravity: float = 900.0
@export var jump_speed: float = 340.0
func _draw() -> void:
    draw_rect(Rect2(-12, -16, 24, 32), Color.SKY_BLUE)
    draw_line(Vector2(-8, -4), Vector2(8, -4), Color.BLACK, 2.0)
func _physics_process(delta: float) -> void:
    velocity.x = Input.get_axis("move_left", "move_right") * speed
    if not is_on_floor():
        velocity.y += gravity * delta
    if Input.is_action_just_pressed("jump") and is_on_floor():
        velocity.y = -jump_speed
    move_and_slide()
```

### Assignment
1. Trace the supplied step and a second step with starting y velocity 20.
2. Annotate where input, acceleration and collision resolution occur.

### Acceptance checks
- First resulting y velocity is -82 and second is 38.
- The trace keeps velocity distinct from displacement.

**Submit:** Two-step table and annotated movement script.

**Question:** Is y velocity -82 rising or falling in this coordinate system?

**Support:** Use the three-jump route and one checkpoint as the minimum game. Remove optional hazards until required tests pass. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Create a second optional route using the same movement code. 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-L074 — Run a fresh-player route test

**Objective:** Use controls and environmental feedback rather than verbal coaching.

**Time:** 55 minutes

### Instruction
Give a willing local tester the readme and start screen, not a live explanation of every obstacle. Observe whether they discover jump, recognize the checkpoint and understand the goal. If testing alone, take a break and follow the readme literally. Record nonidentifying observations and let the tester stop. Fix the most consequential issue first. A player failing a jump is not proof of a defect; look for repeated confusion, unreadable landing information or a mismatch between appearance and collision.

### Worked example
For a repeated route test, keep controls and start position fixed. A table of attempts helps distinguish a consistently hard landing from a one-time input mistake.

Scaffold C-CHECKPOINT — A safe checkpoint respawn
Setup: Add this pattern to a separate Node2D level root with child Player using C-PLATFORM. Call set_checkpoint with a safe global position when a player reaches a checkpoint Area2D; call respawn when entering a fall zone. This code does not connect Area2D automatically; select the area body_entered signal in editor and call these methods from its handler after checking the player group. Respawn is deferred because a physics callback may be active.
```gdscript
extends Node2D
@onready var player: CharacterBody2D = $Player
var checkpoint: Vector2
func _ready() -> void:
    checkpoint = player.global_position
func set_checkpoint(safe_position: Vector2) -> void:
    checkpoint = safe_position
func respawn() -> void:
    _apply_respawn.call_deferred()
func _apply_respawn() -> void:
    player.global_position = checkpoint
    player.velocity = Vector2.ZERO
```

Scaffold C-PLATFORM — A gravity-and-jump platform player
Setup: CharacterBody2D root Player, child CollisionShape2D RectangleShape2D size (24,32), no scale changes. Layer 1 mask 2. StaticBody2D floor layer 2 mask 1 with rectangular collider. Add Input Map move_left, move_right and jump bound to Space. Place player above floor, not intersecting it. Coordinates use positive y downward. Attach script to Player.
```gdscript
extends CharacterBody2D
@export var speed: float = 160.0
@export var gravity: float = 900.0
@export var jump_speed: float = 340.0
func _draw() -> void:
    draw_rect(Rect2(-12, -16, 24, 32), Color.SKY_BLUE)
    draw_line(Vector2(-8, -4), Vector2(8, -4), Color.BLACK, 2.0)
func _physics_process(delta: float) -> void:
    velocity.x = Input.get_axis("move_left", "move_right") * speed
    if not is_on_floor():
        velocity.y += gravity * delta
    if Input.is_action_just_pressed("jump") and is_on_floor():
        velocity.y = -jump_speed
    move_and_slide()
```

### Assignment
1. Run a five-minute local or structured self-test.
2. Record three observations and implement one high-impact fix.

### Acceptance checks
- The fix is linked to evidence.
- A regression test confirms movement, checkpoint and goal still work.

**Submit:** Anonymous test sheet, issue prioritization and revised project.

**Question:** Why rerun old tests after a layout-only fix?

**Support:** Use the three-jump route and one checkpoint as the minimum game. Remove optional hazards until required tests pass. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Create a second optional route using the same movement code. 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-L075 — Submit Relay Runner

**Objective:** Package a short platformer with measured and explainable behavior.

**Time:** 55 minutes

### Instruction
Your final platformer needs a three-jump route, grounded jumping, a midpoint checkpoint, safe fall recovery, a clear goal and Restart. Include a diagram showing intended path and one alternate or assist option. The teacher should be able to inspect the movement script and reproduce a fall recovery from a restored project. Submit your difficulty measurements and repaired-bug evidence. Optional art is welcome, but it does not replace correct collision behavior or a coherent motion explanation.

### Worked example
For a repeated route test, keep controls and start position fixed. A table of attempts helps distinguish a consistently hard landing from a one-time input mistake.

Scaffold C-PLATFORM — A gravity-and-jump platform player
Setup: CharacterBody2D root Player, child CollisionShape2D RectangleShape2D size (24,32), no scale changes. Layer 1 mask 2. StaticBody2D floor layer 2 mask 1 with rectangular collider. Add Input Map move_left, move_right and jump bound to Space. Place player above floor, not intersecting it. Coordinates use positive y downward. Attach script to Player.
```gdscript
extends CharacterBody2D
@export var speed: float = 160.0
@export var gravity: float = 900.0
@export var jump_speed: float = 340.0
func _draw() -> void:
    draw_rect(Rect2(-12, -16, 24, 32), Color.SKY_BLUE)
    draw_line(Vector2(-8, -4), Vector2(8, -4), Color.BLACK, 2.0)
func _physics_process(delta: float) -> void:
    velocity.x = Input.get_axis("move_left", "move_right") * speed
    if not is_on_floor():
        velocity.y += gravity * delta
    if Input.is_action_just_pressed("jump") and is_on_floor():
        velocity.y = -jump_speed
    move_and_slide()
```

Scaffold C-CHECKPOINT — A safe checkpoint respawn
Setup: Add this pattern to a separate Node2D level root with child Player using C-PLATFORM. Call set_checkpoint with a safe global position when a player reaches a checkpoint Area2D; call respawn when entering a fall zone. This code does not connect Area2D automatically; select the area body_entered signal in editor and call these methods from its handler after checking the player group. Respawn is deferred because a physics callback may be active.
```gdscript
extends Node2D
@onready var player: CharacterBody2D = $Player
var checkpoint: Vector2
func _ready() -> void:
    checkpoint = player.global_position
func set_checkpoint(safe_position: Vector2) -> void:
    checkpoint = safe_position
func respawn() -> void:
    _apply_respawn.call_deferred()
func _apply_respawn() -> void:
    player.global_position = checkpoint
    player.velocity = Vector2.ZERO
```

### Assignment
1. Package the project, controls, route diagram and test logs.
2. Verify all required features after restoring the ZIP.

### Acceptance checks
- Fresh launch through completion and restart works.
- Falling after checkpoint returns safely with zeroed velocity.

**Submit:** Relay Runner source artifact, route diagram, test log and motion explanation.

**Question:** Which two variables should respawn restore for immediate physical safety?

**Support:** Use the three-jump route and one checkpoint as the minimum game. Remove optional hazards until required tests pass. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Create a second optional route using the same movement code. 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)

---