Claude
Skills
Sign in
โ† Back

godot-genre-visual-novel

Included with Lifetime
$97 forever

Expert blueprint for visual novels (Doki Doki Literature Club, Phoenix Wright, Steins;Gate) focusing on branching narratives, dialogue systems, choice consequences, rollback mechanics, and persistent flags. Use when building story-driven, choice-based, or dating sim games. Keywords visual novel, dialogue system, branching narrative, typewriter effect, rollback, bbcode, RichTextLabel.

Generalscripts

What this skill does


# Genre: Visual Novel

Branching narratives, meaningful choices, and quality-of-life features define visual novels.

## Core Loop
1.  **Read**: Consume narrative text and character dialogue
2.  **Decide**: Choose at key moments
3.  **Branch**: Story diverges based on choice
4.  **Consequence**: Immediate reaction or long-term flag changes
5.  **Conclude**: Reach one of multiple endings

## NEVER Do (Expert Anti-Patterns)

### Narrative & Flow
- NEVER create the "Illusion of Choice" exclusively; strictly provide **Immediate Dialogue Variations** or **Flag Changes** even if the plot converges later.
- NEVER skip mandatory QoL features; strictly implement **Auto-Play**, **Fast-Forward**, and **Backlog/History** for replayability.
- NEVER display "Walls of Text"; strictly limit dialogue boxes to **3-4 Lines** max to avoid intimidating the reader.
- NEVER hardcode dialogue text inside GDScripts; strictly store narrative scripts in **External Files** (JSON, CSV, or custom Resources) for iteration.
- NEVER ignore the **Rollback** mechanic; strictly maintain a history stack so players can undo miss-clicks or reread missed lines.

### Technical & UI
- NEVER use plain text for emotional beats; strictly use **RichTextLabel BBCode** (e.g., `[shake]`, `[wave]`) to add visual weight.
- NEVER parse massive narrative files on the main thread; strictly use **`ResourceLoader.load_threaded_request()`** to prevent transition stutters.
- NEVER use standard Strings for frequently accessed game flags; strictly use **`StringName`** (&"met_alice") for faster dictionary lookups.
- NEVER use `_process` for letter-by-letter animation; strictly use a **Tween on `visible_ratio`** for smooth, frame-independent reveals.
- NEVER neglect character **Z-ordering**; strictly ensure the active speaker is brought to the front (highest `z_index`) for visual clarity.
- NEVER use `z_index` for `Control` node priority if input handling is required; strictly use `move_to_front()` to ensure draw order and input propagation match.
- NEVER use absolute pixel positioning for character sprites; strictly rely on **Anchors & Percent-based Offsets** for responsive scaling.
- NEVER allow text animations to continue when the player skips; strictly set **`visible_ratio` to 1.0** instantly on input.
- NEVER leave orphaned character sprites; strictly use **`queue_free()`** when actors exit the stage to prevent memory leaks.

---

## ๐Ÿ›  Expert Components (scripts/)

### Original Expert Patterns
- [story_manager.gd](scripts/story_manager.gd) - Flag-aware dialog orchestrator with branching logic and character state persistence.
- [dialogue_ui.gd](scripts/dialogue_ui.gd) - Presentation layer with typewriter tweens and choice-window generation.
- [vn_rollback_manager.gd](scripts/vn_rollback_manager.gd) - History stack maintenance for state rollback (flags/backgrounds/index).

### Modular Components
- [visual_novel_patterns.gd](scripts/visual_novel_patterns.gd) - Reusable patterns: BBCode effects, choice filtering, and sprite layering.
- [dialogue_ui.gd](scripts/dialogue_ui.gd) - Base UI core for dialogue and character management.

---

| Phase | Skills | Purpose |
|-------|--------|---------|
| 1. Text & UI | `ui-system`, `rich-text-label` | Dialogue box, bbcode effects, typewriting |
| 2. Logic | `json-parsing`, `resource-management` | Loading scripts, managing character data |
| 3. State | `godot-save-load-systems`, `dictionaries` | Flags, history, persistent data |
| 4. Audio | `audio-system` | Voice acting, background music transitions |
| 5. Polish | `godot-tweening`, `shaders` | Character transitions, background effects |

## Architecture Overview

### 1. Story Manager (The Driver)
Parses the script and directs the other systems.

```gdscript
# story_manager.gd
extends Node

var current_script: Dictionary
var current_line_index: int = 0
var flags: Dictionary = {}

func load_script(script_path: String) -> void:
    var file = FileAccess.open(script_path, FileAccess.READ)
    current_script = JSON.parse_string(file.get_as_text())
    current_line_index = 0
    display_next_line()

func display_next_line() -> void:
    if current_line_index >= current_script["lines"].size():
        return
        
    var line_data = current_script["lines"][current_line_index]
    
    if line_data.has("choice"):
        present_choices(line_data["choice"])
    else:
        CharacterManager.show_character(line_data.get("character"), line_data.get("expression"))
        DialogueUI.show_text(line_data["text"])
        current_line_index += 1
```

### 2. Dialogue UI (Typewriter Effect)
Displaying text character by character.

```gdscript
# dialogue_ui.gd
func show_text(text: String) -> void:
    rich_text_label.text = text
    rich_text_label.visible_ratio = 0.0
    
    var tween = create_tween()
    tween.tween_property(rich_text_label, "visible_ratio", 1.0, text.length() * 0.05)
```

### 3. History & Rollback
Essential VN feature. Store the state before every line.

```gdscript
var history: Array[Dictionary] = []

func save_state_to_history() -> void:
    history.append({
        "line_index": current_line_index,
        "flags": flags.duplicate(),
        "background": current_background,
        "music": current_music
    })

func rollback() -> void:
    if history.is_empty(): return
    var trusted_state = history.pop_back()
    restore_state(trusted_state)
```

## Key Mechanics Implementation

### Branching Paths (Flags)
Track decisions to influence future scenes.

```gdscript
func make_choice(choice_id: String) -> void:
    match choice_id:
        "be_nice":
            flags["relationship_alice"] += 1
            jump_to_label("alice_happy")
        "be_mean":
            flags["relationship_alice"] -= 1
            jump_to_label("alice_sad")

### 4. Speaker Z-Ordering (Dynamic Focus)
Bring the active speaker to the front and dim others.

```gdscript
# actor_manager.gd
func focus_speaker(active_name: String) -> void:
    for actor in get_children():
        if not actor is CanvasItem: continue
            
        if actor.name == active_name:
            # Move to bottom of tree to render on top & catch input events
            actor.move_to_front()
            actor.modulate = Color.WHITE
        else:
            # Dim inactive actors
            actor.modulate = Color(0.5, 0.5, 0.5, 1.0)
```

### 5. Asynchronous Background Loading
Prevent stutters when switching high-res assets.

```gdscript
# background_streamer.gd
var _pending_path: String = ""

func load_background(path: String) -> void:
    _pending_path = path
    ResourceLoader.load_threaded_request(path)
    set_process(true)

func _process(_delta: float) -> void:
    var status = ResourceLoader.load_threaded_get_status(_pending_path)
    if status == ResourceLoader.THREAD_LOAD_LOADED:
        texture = ResourceLoader.load_threaded_get(_pending_path)
        set_process(false)
```

### 6. Emotional BBCode Effects
Use `RichTextLabel` with performance-first `append_text`.

```gdscript
# dialogue_printer.gd
func print_line(speaker: String, text: String, emotion: String) -> void:
    var bb: String = text
    match emotion:
        "angry": bb = "[shake rate=30.0 level=8]%s[/shake]" % text
        "sad": bb = "[wave amp=20.0 freq=2.0]%s[/wave]" % text
    
    # Use append_text to avoid rebuilding the entire tag stack
    append_text("[b]%s:[/b] %s\n" % [speaker, bb])
```
```

### Script Format (JSON vs Resource)
*   **JSON**: Easy to write externally, standard format.
*   **Custom Resource**: Typosafe, editable in Inspector.
*   **Text Parsers**: (e.g., Markdown-like syntax) simpler for writers.

## Common Pitfalls

1.  **Too Much Text**: Walls of text are intimidating. Break it up. **Fix**: Limit lines to 3-4 rows max.
2.  **Illusion of Choice**: Choices that lead to the same outcome immediately feel cheap. **Fix**: Use small variations in dialogue even if the main plot converges.
3.  **Missing Quality of Life**: No Skip, No Auto, No Save. **Fix**: These

Related in General