From 5cde8be9a792d829fc3513217e9a4713f0b5036c Mon Sep 17 00:00:00 2001 From: Bryce Date: Fri, 1 Aug 2025 20:07:40 -0700 Subject: [PATCH] improvements --- .../dialogue_character_acceptance.md | 149 +++++++++++++++ architecture/dialogue_system_architecture.md | 93 +++++++++ main.tscn | 3 + plans/base_character_dialogue_plan.md | 82 ++++++++ scenes/base_character.tscn | 10 + scenes/test_dialogue.tscn | 23 +++ scripts/base_character.gd | 35 ++++ scripts/dialogue_system.gd | 176 ++++++++++++++++++ scripts/test_dialogue.gd | 10 + 9 files changed, 581 insertions(+) create mode 100644 acceptance_criteria/dialogue_character_acceptance.md create mode 100644 architecture/dialogue_system_architecture.md create mode 100644 main.tscn create mode 100644 plans/base_character_dialogue_plan.md create mode 100644 scenes/base_character.tscn create mode 100644 scenes/test_dialogue.tscn create mode 100644 scripts/base_character.gd create mode 100644 scripts/dialogue_system.gd create mode 100644 scripts/test_dialogue.gd diff --git a/acceptance_criteria/dialogue_character_acceptance.md b/acceptance_criteria/dialogue_character_acceptance.md new file mode 100644 index 0000000..1a632bf --- /dev/null +++ b/acceptance_criteria/dialogue_character_acceptance.md @@ -0,0 +1,149 @@ +# Acceptance Criteria: Base Character with Dialogue System + +## Overview +This document defines the acceptance criteria for implementing a base character scene with dialogue functionality in Godot. The implementation should allow characters to have dialogue lines that can be triggered programmatically. + +## Requirements + +### 1. Base Character Scene +- **Feature**: Create a base character scene with Polygon2D sprite +- **Acceptance Criteria**: + - Scene file `scenes/base_character.tscn` exists + - Contains a Polygon2D node as the character sprite + - Character has appropriate positioning and scaling + - Scene can be instantiated in other scenes + - Scene includes necessary child nodes for dialogue system integration + +### 2. Dialogue Entity +- **Feature**: Implement dialogue entity that can be triggered programmatically +- **Acceptance Criteria**: + - Dialogue entity can be attached to any character node + - Can be triggered via a public method call + - Accepts text content and duration parameters + - Supports programmatic triggering without user input + +### 3. Visual Dialogue Box +- **Feature**: White dialogue box that animates in when triggered +- **Acceptance Criteria**: + - Dialogue box appears above character when triggered + - Animates in with smooth transition (fade/slide) + - Has white background with appropriate styling + - Properly positioned relative to character + - Resizes based on text content + +### 4. Text Reveal Animation +- **Feature**: Text revealed one character at a time +- **Acceptance Criteria**: + - Text appears character-by-character with delay + - Each character reveal has consistent timing + - Text is properly formatted and readable + - Animation can be configured with different speeds + +### 5. Duration Handling +- **Feature**: Dialogue box closes after specified duration +- **Acceptance Criteria**: + - Dialogue automatically closes after duration ends + - Smooth animation out when closing + - Duration parameter is configurable + - Can handle zero or negative durations appropriately + +### 6. API Interface +- **Feature**: Clean public API for dialogue interaction +- **Acceptance Criteria**: + - Public method to trigger dialogue: `trigger_dialogue(text, duration)` + - Method returns immediately after triggering + - No blocking operations during animation + - Proper error handling for invalid parameters + +### 7. Test Scene +- **Feature**: Single character automatically triggering a single dialogue line +- **Acceptance Criteria**: + - Test scene file `scenes/test_dialogue.tscn` exists + - Contains one base character instance + - Dialogue is automatically triggered on scene load + - Demonstrates complete functionality (show, reveal, hide) + - Scene runs without errors + +## Technical Specifications + +### File Structure +``` +scenes/ +├── base_character.tscn +└── test_dialogue.tscn + +scripts/ +├── dialogue_system.gd +└── base_character.gd +``` + +### Dialogue System Components +1. **DialogueBox**: Visual UI element for displaying dialogue +2. **TextRevealer**: Handles character-by-character text animation +3. **AnimationController**: Manages show/hide animations +4. **CharacterDialog**: Interface for character-to-dialogue communication + +### API Methods +```gdscript +# In base_character.gd or similar +func trigger_dialogue(text: String, duration: float) -> void: + # Triggers dialogue with specified text and duration + +# In dialogue_system.gd +func show_dialogue(text: String, duration: float) -> void: + # Internal method to display dialogue UI +``` + +### Animation Requirements +- **Show Animation**: 0.3 second fade/slide in +- **Text Reveal**: 0.05 second per character +- **Hide Animation**: 0.2 second fade out +- **Duration**: Default 3 seconds if not specified + +## Success Metrics + +### Functional Tests +1. [ ] Base character scene loads without errors +2. [ ] Dialogue box appears when `trigger_dialogue()` is called +3. [ ] Text reveals character-by-character with correct timing +4. [ ] Dialogue box closes automatically after duration +5. [ ] Test scene demonstrates complete workflow +6. [ ] API methods can be called from other scripts + +### Performance Tests +1. [ ] No frame drops during animation sequences +2. [ ] Memory usage remains stable +3. [ ] Animation timing is consistent across different hardware + +### Compatibility Tests +1. [ ] Works with existing Godot 4.x projects +2. [ ] Compatible with different character positioning +3. [ ] Handles various text lengths appropriately +4. [ ] Works with different duration values + +## Non-Functional Requirements + +### Code Quality +- [ ] Well-documented code with comments +- [ ] Clean, maintainable code structure +- [ ] Follows Godot coding conventions +- [ ] Proper error handling and validation + +### User Experience +- [ ] Smooth animations without jank +- [ ] Clear visual feedback during dialogue +- [ ] Responsive interface +- [ ] Accessible text formatting + +## Dependencies +- Godot 4.x engine +- Standard Godot 2D node types (Node2D, Control, Label) +- AnimationPlayer or Tween for UI animations +- Timer for duration handling + +## Acceptance Process +1. Review code implementation against these criteria +2. Run test scene to verify functionality +3. Confirm all acceptance tests pass +4. Validate API usability and documentation +5. Ensure no breaking changes to existing systems \ No newline at end of file diff --git a/architecture/dialogue_system_architecture.md b/architecture/dialogue_system_architecture.md new file mode 100644 index 0000000..84350e1 --- /dev/null +++ b/architecture/dialogue_system_architecture.md @@ -0,0 +1,93 @@ +# Dialogue System Architecture + +```mermaid +graph TD + A[Base Character Scene] --> B(DialogueSystem) + B --> C(DialogueBox UI) + B --> D(TextRevealer) + B --> E(AnimationController) + B --> F(Timer) + + C --> G[Visual Display] + D --> H[Character-by-Character Reveal] + E --> I[Show/Hide Animations] + F --> J[Duration Management] + + subgraph "Dialogue Flow" + B --> K{Trigger Dialogue} + K -->|Text + Duration| L[Show Dialogue Box] + L --> M[Start Text Reveal] + M --> N[Wait for Duration] + N --> O[Hide Dialogue Box] + end + + subgraph "Components" + C --> C1[White Background] + C --> C2[Positioning] + D --> D1[Character Timing] + D --> D2[Text Display] + E --> E1[Animation Playback] + F --> F1[Timer Management] + end + + style A fill:#e1f5fe + style B fill:#f3e5f5 + style C fill:#e8f5e9 + style D fill:#fff3e0 + style E fill:#fce4ec + style F fill:#f1f8e9 +``` + +## Component Descriptions + +### Base Character Scene (A) +- Contains the character sprite (Polygon2D) +- Acts as the entry point for dialogue triggering +- Manages the dialogue system interface + +### DialogueSystem (B) - Main Controller +- Coordinates all dialogue functionality +- Interfaces with UI components +- Handles timing and state management +- Provides public API for triggering dialogue + +### DialogueBox UI (C) +- Visual representation of dialogue +- White background container +- Proper positioning relative to character +- Handles visibility states + +### TextRevealer (D) +- Manages character-by-character text display +- Controls reveal timing +- Updates text label content incrementally +- Handles text formatting and wrapping + +### AnimationController (E) +- Manages show/hide animations +- Controls timing of UI transitions +- Ensures smooth visual experience +- Handles animation cleanup + +### Timer (F) +- Manages dialogue duration +- Triggers automatic closure +- Provides timing feedback to system +- Handles edge cases (zero/negative durations) + +## Flow Description + +1. **Trigger**: Base character calls `trigger_dialogue(text, duration)` +2. **Show**: DialogueSystem initiates show animation for DialogueBox +3. **Reveal**: TextRevealer starts character-by-character text display +4. **Wait**: Timer waits for specified duration +5. **Hide**: AnimationController triggers hide animation +6. **Complete**: System returns to idle state + +## Integration Points + +- Base Character Scene: Public API interface +- DialogueBox UI: Visual rendering component +- TextRevealer: Text animation logic +- AnimationController: UI transition handling +- Timer: Duration management \ No newline at end of file diff --git a/main.tscn b/main.tscn new file mode 100644 index 0000000..d036eb5 --- /dev/null +++ b/main.tscn @@ -0,0 +1,3 @@ +[gd_scene format=3 uid="uid://dihuaoy6htse6"] + +[node name="Node2D" type="Node2D"] diff --git a/plans/base_character_dialogue_plan.md b/plans/base_character_dialogue_plan.md new file mode 100644 index 0000000..32172e9 --- /dev/null +++ b/plans/base_character_dialogue_plan.md @@ -0,0 +1,82 @@ +# Base Character Scene with Dialogue System Design Plan + +## Overview +Create a base character scene with: +1. A sprite/shape (using Polygon2D for now) +2. A dialogue entity that can be triggered programmatically +3. Visual dialogue box with animation and character-by-character text reveal +4. Test scene to demonstrate the behavior + +## Implementation Steps + +### 1. Base Character Scene Design +- Create a new scene file `scenes/base_character.tscn` +- Add a Polygon2D node as the character sprite +- Add necessary child nodes for dialogue system integration +- Set up proper positioning and scaling + +### 2. Dialogue UI System +- Create dialogue box UI with: + - Background rectangle (white) + - Text display area + - Animation for showing/hiding +- Implement character-by-character text reveal +- Handle timing and duration logic + +### 3. Dialogue Action Integration +- Extend or modify DialogueAction to use new UI system +- Ensure compatibility with existing cutscene manager +- Add proper signal handling for animation completion + +### 4. Test Scene +- Create test scene `scenes/test_dialogue.tscn` +- Add base character instance +- Automatically trigger dialogue on scene load +- Demonstrate the complete functionality + +## Technical Details + +### Base Character Scene Structure +``` +Node2D (root) +├── Polygon2D (character sprite) +└── DialogueSystem (script for handling dialogue UI) +``` + +### Dialogue System Components +1. **DialogueBox**: Visual element that appears when dialogue is triggered +2. **TextRevealer**: Handles character-by-character text display +3. **AnimationController**: Manages show/hide animations +4. **Timer**: Controls duration of dialogue display + +### DialogueAction Integration Points +- `start()` method should trigger UI display +- Text and duration parameters should be passed to UI system +- Completion signal should be properly emitted + +## File Structure +``` +scenes/ +├── base_character.tscn +└── test_dialogue.tscn + +scripts/ +├── dialogue_system.gd +└── base_character.gd + +cutscene/ +└── actions/ + └── DialogueAction.gd (modified) +``` + +## Implementation Approach +1. First, create the base character scene with Polygon2D +2. Implement the dialogue UI system in a separate script +3. Modify DialogueAction to integrate with new UI system +4. Create test scene that demonstrates functionality +5. Ensure all components work together within existing cutscene framework + +## Animation Requirements +- Dialogue box should animate in (fade/slide) +- Text should reveal character-by-character with delay +- Dialogue box should animate out after duration ends \ No newline at end of file diff --git a/scenes/base_character.tscn b/scenes/base_character.tscn new file mode 100644 index 0000000..04c9cec --- /dev/null +++ b/scenes/base_character.tscn @@ -0,0 +1,10 @@ +[gd_scene format=3 uid="uid://basecharacter"] + +[node name="BaseCharacter" type="Node2D"] + +[node name="Sprite" type="Polygon2D" parent="."] +color = Color(0.2, 0.6, 1, 1) +antialiased = true +polygon = PackedVector2Array(10, -20, 20, 0, 10, 20, -10, 20, -20, 0, -10, -20) + +[node name="DialogueSystem" type="Node" parent="."] diff --git a/scenes/test_dialogue.tscn b/scenes/test_dialogue.tscn new file mode 100644 index 0000000..f14dd32 --- /dev/null +++ b/scenes/test_dialogue.tscn @@ -0,0 +1,23 @@ +[gd_scene load_steps=4 format=3 uid="uid://testdialogue"] + +[ext_resource type="Script" path="res://scripts/test_dialogue.gd" id="1_f305o"] +[ext_resource type="Script" path="res://scripts/base_character.gd" id="2_00bif"] +[ext_resource type="Script" path="res://scripts/dialogue_system.gd" id="3_11ynk"] + +[node name="TestScene" type="Node2D"] +position = Vector2(-1, 0) +script = ExtResource("1_f305o") + +[node name="Character" type="Node2D" parent="."] +position = Vector2(407, 206) + +[node name="BaseCharacter" type="Node2D" parent="Character"] +script = ExtResource("2_00bif") + +[node name="Sprite" type="Polygon2D" parent="Character/BaseCharacter"] +color = Color(0.2, 0.6, 1, 1) +antialiased = true +polygon = PackedVector2Array(10, -20, 20, 0, 10, 20, -10, 20, -20, 0, -10, -20) + +[node name="DialogueSystem" type="Node2D" parent="Character/BaseCharacter"] +script = ExtResource("3_11ynk") diff --git a/scripts/base_character.gd b/scripts/base_character.gd new file mode 100644 index 0000000..b60da15 --- /dev/null +++ b/scripts/base_character.gd @@ -0,0 +1,35 @@ +extends Node2D + +# Base character class that can display dialogue +# This extends Node2D to be compatible with Godot's scene system + +# Reference to the dialogue system +var dialogue_system: Node = null + +# Signals +signal dialogue_started() +signal dialogue_completed() + +func _ready(): + # Find and setup dialogue system + dialogue_system = get_node_or_null("DialogueSystem") + if dialogue_system: + dialogue_system.connect("dialogue_started", self._on_dialogue_started) + dialogue_system.connect("dialogue_completed", self._on_dialogue_completed) + else: + print("Warning: No dialogue system found on character") + +func trigger_dialogue(text: String, duration: float = 3.0) -> void: + """Public method to trigger dialogue on this character""" + if dialogue_system: + dialogue_system.trigger_dialogue(text, duration) + else: + print("Warning: No dialogue system found on character") + +func _on_dialogue_started(): + """Handle dialogue start signal""" + emit_signal("dialogue_started") + +func _on_dialogue_completed(): + """Handle dialogue completion signal""" + emit_signal("dialogue_completed") diff --git a/scripts/dialogue_system.gd b/scripts/dialogue_system.gd new file mode 100644 index 0000000..990f82e --- /dev/null +++ b/scripts/dialogue_system.gd @@ -0,0 +1,176 @@ +extends Node2D + +# Dialogue system for Godot 4.x +# Handles displaying dialogue with animations and text reveal + +# Signals +signal dialogue_started() +signal dialogue_completed() + +# Configuration +@export var text_reveal_speed: float = 0.05 # seconds per character +@export var show_animation_duration: float = 0.3 # seconds +@export var hide_animation_duration: float = 0.2 # seconds + +# Private variables +var dialogue_box: Control = null +var text_label: Label = null +var timer: Timer = null +var current_text: String = "" +var current_duration: float = 0.0 +var text_index: int = 0 +var is_showing: bool = false +var is_revealing: bool = false + +func _ready(): + # Initialize the dialogue system + _create_dialogue_ui() + +func _create_dialogue_ui(): + # Create dialogue box UI + dialogue_box = Control.new() + dialogue_box.name = "DialogueBox" + + # Create background + var background = ColorRect.new() + background.name = "Background" + background.color = Color(1, 1, 1, 1) # White background + background.size = Vector2(300, 80) + background.anchor_left = 0.5 + background.anchor_top = 1.0 + background.anchor_right = 0.5 + background.anchor_bottom = 1.0 + background.position = Vector2(-150, -40) # Positioned above character + background.pivot_offset = Vector2(150, 40) # Center pivot + + # Create text label + text_label = Label.new() + text_label.name = "TextLabel" + text_label.text = "" + text_label.size = Vector2(280, 60) + text_label.anchor_left = 0.5 + text_label.add_theme_color_override("font_color", Color.BLACK) + text_label.anchor_top = 0.5 + text_label.anchor_right = 0.5 + text_label.anchor_bottom = 0.5 + text_label.position = Vector2(-140, -30) # Centered in background + text_label.pivot_offset = Vector2(140, 30) # Center pivot + text_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT + text_label.vertical_alignment = VERTICAL_ALIGNMENT_TOP + text_label.autowrap_mode = 2 # TextServer.AUTOWRAP_WORD_SMART + #text_label.custom_styles.normal.font_size = 16 + + # Add to dialogue box + dialogue_box.add_child(background) + dialogue_box.add_child(text_label) + + # Set initial state - hidden and transparent + dialogue_box.visible = false + dialogue_box.modulate.a = 0.0 + + # Add to scene tree (should be added to parent character) + add_child(dialogue_box) + + # Position the dialogue box above the character + # This ensures it's positioned correctly relative to the character + if get_parent() != null: + var parent_pos = get_parent().position + dialogue_box.position = Vector2(parent_pos.x, parent_pos.y - 50) # Position above character + +func trigger_dialogue(text: String, duration: float = 3.0) -> void: + """Trigger dialogue with specified text and duration""" + if is_showing: + return + + current_text = text + current_duration = duration + text_index = 0 + is_showing = true + is_revealing = false + + # Show the dialogue box with animation + show_dialogue_box() + + # Start revealing text after a short delay to allow animation to complete + await get_tree().create_timer(show_animation_duration) + + # Start revealing text immediately + start_text_reveal() + +func show_dialogue_box(): + """Show dialogue box with animation""" + dialogue_box.visible = true + + # Animate in using Tween + var tween = create_tween() + tween.set_ease(Tween.EASE_OUT) + tween.set_trans(Tween.TRANS_SINE) + tween.tween_property(dialogue_box, "modulate:a", 1.0, show_animation_duration) + + # Emit signal when animation completes + await tween.finished + emit_signal("dialogue_started") + +func hide_dialogue_box(): + """Hide dialogue box with animation""" + # Animate out using Tween + var tween = create_tween() + tween.set_ease(Tween.EASE_IN) + tween.set_trans(Tween.TRANS_SINE) + tween.tween_property(dialogue_box, "modulate:a", 0.0, hide_animation_duration) + + # Wait for animation to complete + await tween.finished + + dialogue_box.visible = false + is_showing = false + is_revealing = false + emit_signal("dialogue_completed") + +func start_text_reveal(): + """Start revealing text character by character""" + if is_revealing: + return + + is_revealing = true + text_label.text = "" + text_index = 0 + + # If duration is 0 or negative, don't auto-hide + if current_duration <= 0: + # Just reveal all text immediately + text_label.text = current_text + is_revealing = false + return + + # Start timer for automatic hiding + timer = Timer.new() + timer.wait_time = current_duration + timer.one_shot = true + + timer.connect("timeout", self._on_dialogue_timeout) + add_child(timer) + timer.start() + + # Start the character-by-character reveal + reveal_next_character() + +func reveal_next_character(): + """Reveal the next character in the text""" + if text_index >= current_text.length(): + # Finished revealing all characters + is_revealing = false + return + + # Add the next character to the text label + text_label.text += current_text[text_index] + text_index += 1 + + # Schedule next character reveal + await get_tree().create_timer(text_reveal_speed) + reveal_next_character() + +func _on_dialogue_timeout(): + """Handle dialogue timeout""" + if is_showing and not is_revealing: + hide_dialogue_box() diff --git a/scripts/test_dialogue.gd b/scripts/test_dialogue.gd new file mode 100644 index 0000000..65dad12 --- /dev/null +++ b/scripts/test_dialogue.gd @@ -0,0 +1,10 @@ +extends Node2D + +func _ready(): + # Find the character and trigger dialogue after a short delay + var character = $Character/BaseCharacter + print("HELLO AND HERE") + if character: + # Wait a bit to let the scene load properly, then trigger dialogue + await get_tree().create_timer(1.0).timeout + character.trigger_dialogue("Hello! This is a test dialogue line that will reveal character by character.", 5.0)