Video Game Design and Coding

Week 10 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 10 — Story as interactive structure

## VGC-L046 — Map a small branching story

**Objective:** Design a choice graph with reachable endings.

**Time:** 55 minutes

### Instruction
Archive Lantern is a fictional exploration puzzle told through buttons and short passages. A story graph has nodes for passages and arrows for choices. Start with an entrance, a keeper, a sign, a locked room and two endings. Every passage needs a way forward or a deliberate ending; accidental dead ends trap the player. Choices should describe actions rather than hide arbitrary right answers. Use fictional characters and places. Nothing in the story should ask the player for their real identity or contact information.

### Worked example
A passage with key entrance can display any original sentence. Its choice ["Read the note", "note"] links to a stable note key even if the sentence is later edited.

### Assignment
1. Draw a six-node graph including two endings and one rejoining branch.
2. Label each arrow with the exact button text.

### Acceptance checks
- Every node is reachable from the entrance.
- Every nonending node has at least one outgoing choice.

**Submit:** Story graph photo/scan or private diagram document.

**Question:** What is the difference between a deliberate ending and a broken dead end?

**Support:** Use three screens and two buttons with C-DIALOGUE. Draw every link before writing dialogue. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Create a branch that rejoins the main path while preserving a meaningful choice. 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-L047 — Build a readable dialogue interface

**Objective:** Use Control containers instead of arbitrary overlapping positions.

**Time:** 55 minutes

### Instruction
Godot Control nodes are for interfaces. C-DIALOGUE uses a VBoxContainer to stack a Label and two Buttons. A container manages its children’s placement, so manually dragging a child may be overridden. Set the root to a full-rect layout, allow the label to wrap and keep buttons large enough to read. Test one long passage before filling the story. Good layout provides space for different text lengths rather than assuming every line will be equally short.

### Worked example
A passage with key entrance can display any original sentence. Its choice ["Read the note", "note"] links to a stable note key even if the sentence is later edited.

Scaffold C-DIALOGUE — Data-driven branching dialogue without personal data
Setup: Create Control root Dialogue with VBoxContainer Box containing Label Line and Buttons A and B. Set Box anchors to full rect; Line autowrap if needed. Attach script to Dialogue. Data keys are fictional scene IDs. All three screens can fit in 640x360. This is a self-contained example; replace story text in the assignment.
```gdscript
extends Control
var page: String = "start"
var story: Dictionary = {
    "start": {"text": "Two paths enter the archive.", "choices": [["Ask the keeper", "keeper"], ["Read a sign", "sign"]]},
    "keeper": {"text": "The keeper offers a map.", "choices": [["Return", "start"]]},
    "sign": {"text": "Quiet room to the east.", "choices": [["Return", "start"]]}
}
func _ready() -> void:
    $Box/A.pressed.connect(_choose.bind(0))
    $Box/B.pressed.connect(_choose.bind(1))
    _show()
func _show() -> void:
    var entry: Dictionary = story[page]
    $Box/Line.text = entry["text"]
    var buttons: Array[Button] = [$Box/A, $Box/B]
    for i in range(buttons.size()):
        buttons[i].visible = i < entry["choices"].size()
        if buttons[i].visible:
            buttons[i].text = entry["choices"][i][0]
    $Box/A.grab_focus()
func _choose(index: int) -> void:
    var choices: Array = story[page]["choices"]
    if index < 0 or index >= choices.size():
        return
    page = str(choices[index][1])
    _show()
```

### Assignment
1. Build the Dialogue scene with Box, Line, A and B.
2. Test one short and one 40-word passage at the course viewport size.

### Acceptance checks
- Text does not overlap buttons or run off-screen.
- Both button labels are readable without truncating the action.

**Submit:** Dialogue UI scene and two layout screenshots.

**Question:** Why can a VBoxContainer undo a child’s manual position?

**Support:** Use three screens and two buttons with C-DIALOGUE. Draw every link before writing dialogue. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Create a branch that rejoins the main path while preserving a meaningful choice. 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-L048 — Store story content as data

**Objective:** Use dictionary keys and arrays to separate content from behavior.

**Time:** 55 minutes

### Instruction
C-DIALOGUE stores passages in a Dictionary keyed by stable IDs. Each passage has text and a choices array; each choice stores visible label and target ID. The rendering code does not need a new function for every passage. IDs are not the same as displayed prose: changing a sentence should not break links. Match quote marks, commas, brackets and braces carefully. The starter story is a worked example, not your final narrative assignment. Replace its prose with your own graph’s content.

### Worked example
A passage with key entrance can display any original sentence. Its choice ["Read the note", "note"] links to a stable note key even if the sentence is later edited.

Scaffold C-DIALOGUE — Data-driven branching dialogue without personal data
Setup: Create Control root Dialogue with VBoxContainer Box containing Label Line and Buttons A and B. Set Box anchors to full rect; Line autowrap if needed. Attach script to Dialogue. Data keys are fictional scene IDs. All three screens can fit in 640x360. This is a self-contained example; replace story text in the assignment.
```gdscript
extends Control
var page: String = "start"
var story: Dictionary = {
    "start": {"text": "Two paths enter the archive.", "choices": [["Ask the keeper", "keeper"], ["Read a sign", "sign"]]},
    "keeper": {"text": "The keeper offers a map.", "choices": [["Return", "start"]]},
    "sign": {"text": "Quiet room to the east.", "choices": [["Return", "start"]]}
}
func _ready() -> void:
    $Box/A.pressed.connect(_choose.bind(0))
    $Box/B.pressed.connect(_choose.bind(1))
    _show()
func _show() -> void:
    var entry: Dictionary = story[page]
    $Box/Line.text = entry["text"]
    var buttons: Array[Button] = [$Box/A, $Box/B]
    for i in range(buttons.size()):
        buttons[i].visible = i < entry["choices"].size()
        if buttons[i].visible:
            buttons[i].text = entry["choices"][i][0]
    $Box/A.grab_focus()
func _choose(index: int) -> void:
    var choices: Array = story[page]["choices"]
    if index < 0 or index >= choices.size():
        return
    page = str(choices[index][1])
    _show()
```

### Assignment
1. Enter your six passages into a story dictionary using stable IDs.
2. Make a table of every target ID and verify it exists.

### Acceptance checks
- All target keys exist.
- Story text can change without changing its passage ID.

**Submit:** Original story data and target-ID audit.

**Question:** Why should “The room is quiet.” not serve as a passage identifier?

**Support:** Use three screens and two buttons with C-DIALOGUE. Draw every link before writing dialogue. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Create a branch that rejoins the main path while preserving a meaningful choice. 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-L049 — Connect choices without duplicate handlers

**Objective:** Bind button indexes and update a view from state.

**Time:** 55 minutes

### Instruction
A button’s pressed signal does not say which story choice it represents. C-DIALOGUE binds index zero or one when connecting each button. _choose looks up the current page’s choices, validates the index, changes page and refreshes the view. _show hides an unused second button on one-choice pages. Connect buttons once in _ready; repeatedly connecting every time a passage is shown can trigger multiple transitions from one press. The page variable is state and the label is only its view.

### Worked example
A passage with key entrance can display any original sentence. Its choice ["Read the note", "note"] links to a stable note key even if the sentence is later edited.

Scaffold C-DIALOGUE — Data-driven branching dialogue without personal data
Setup: Create Control root Dialogue with VBoxContainer Box containing Label Line and Buttons A and B. Set Box anchors to full rect; Line autowrap if needed. Attach script to Dialogue. Data keys are fictional scene IDs. All three screens can fit in 640x360. This is a self-contained example; replace story text in the assignment.
```gdscript
extends Control
var page: String = "start"
var story: Dictionary = {
    "start": {"text": "Two paths enter the archive.", "choices": [["Ask the keeper", "keeper"], ["Read a sign", "sign"]]},
    "keeper": {"text": "The keeper offers a map.", "choices": [["Return", "start"]]},
    "sign": {"text": "Quiet room to the east.", "choices": [["Return", "start"]]}
}
func _ready() -> void:
    $Box/A.pressed.connect(_choose.bind(0))
    $Box/B.pressed.connect(_choose.bind(1))
    _show()
func _show() -> void:
    var entry: Dictionary = story[page]
    $Box/Line.text = entry["text"]
    var buttons: Array[Button] = [$Box/A, $Box/B]
    for i in range(buttons.size()):
        buttons[i].visible = i < entry["choices"].size()
        if buttons[i].visible:
            buttons[i].text = entry["choices"][i][0]
    $Box/A.grab_focus()
func _choose(index: int) -> void:
    var choices: Array = story[page]["choices"]
    if index < 0 or index >= choices.size():
        return
    page = str(choices[index][1])
    _show()
```

### Assignment
1. Implement the story with C-DIALOGUE’s connection pattern.
2. Visit both branches and return repeatedly.

### Acceptance checks
- One press produces one transition.
- A one-choice passage cannot activate a stale second choice.

**Submit:** Runnable branching-story project and repeat-navigation log.

**Question:** Why connect once rather than inside _show?

**Support:** Use three screens and two buttons with C-DIALOGUE. Draw every link before writing dialogue. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Create a branch that rejoins the main path while preserving a meaningful choice. 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-L050 — Navigate the story with a keyboard

**Objective:** Verify focus, activation and a predictable return path.

**Time:** 55 minutes

### Instruction
An interface is not keyboard-accessible simply because it contains buttons. Focus identifies which control receives activation. C-DIALOGUE calls grab_focus on A after showing a new passage. Test Tab, Shift-Tab and the normal accept key without clicking the mouse. Focus order should follow the reading order and never land on a hidden button. Provide an ending Restart action that returns to the entrance and resets story variables. State changes should not leave focus on a button from the previous page.

### Worked example
A passage with key entrance can display any original sentence. Its choice ["Read the note", "note"] links to a stable note key even if the sentence is later edited.

Scaffold C-DIALOGUE — Data-driven branching dialogue without personal data
Setup: Create Control root Dialogue with VBoxContainer Box containing Label Line and Buttons A and B. Set Box anchors to full rect; Line autowrap if needed. Attach script to Dialogue. Data keys are fictional scene IDs. All three screens can fit in 640x360. This is a self-contained example; replace story text in the assignment.
```gdscript
extends Control
var page: String = "start"
var story: Dictionary = {
    "start": {"text": "Two paths enter the archive.", "choices": [["Ask the keeper", "keeper"], ["Read a sign", "sign"]]},
    "keeper": {"text": "The keeper offers a map.", "choices": [["Return", "start"]]},
    "sign": {"text": "Quiet room to the east.", "choices": [["Return", "start"]]}
}
func _ready() -> void:
    $Box/A.pressed.connect(_choose.bind(0))
    $Box/B.pressed.connect(_choose.bind(1))
    _show()
func _show() -> void:
    var entry: Dictionary = story[page]
    $Box/Line.text = entry["text"]
    var buttons: Array[Button] = [$Box/A, $Box/B]
    for i in range(buttons.size()):
        buttons[i].visible = i < entry["choices"].size()
        if buttons[i].visible:
            buttons[i].text = entry["choices"][i][0]
    $Box/A.grab_focus()
func _choose(index: int) -> void:
    var choices: Array = story[page]["choices"]
    if index < 0 or index >= choices.size():
        return
    page = str(choices[index][1])
    _show()
```

### Assignment
1. Complete one story path using keyboard only.
2. Add or implement Restart and test a second path.

### Acceptance checks
- Focus is visible and reaches every available action.
- Hidden choices cannot receive activation.

**Submit:** Keyboard-navigation checklist and updated project.

**Question:** Why reset focus after changing passages?

**Support:** Use three screens and two buttons with C-DIALOGUE. Draw every link before writing dialogue. Start with the first assigned task and re-run its acceptance test before adding the remaining feature.

**Optional extension:** Create a branch that rejoins the main path while preserving a meaningful choice. 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)

---