275 lines
8.0 KiB
Markdown
275 lines
8.0 KiB
Markdown
# Action Completion and Callback System Design
|
|
|
|
## Overview
|
|
This document details the completion and callback system that allows actions to notify the CutsceneManager when they have finished executing, and enables flexible callback mechanisms for complex action sequences.
|
|
|
|
## Signal-Based Completion System
|
|
|
|
### Core Signals
|
|
Each action implements these core signals:
|
|
|
|
```gdscript
|
|
# Emitted when the action starts executing
|
|
signal started()
|
|
|
|
# Emitted when the action completes successfully
|
|
signal completed()
|
|
|
|
# Emitted when the action fails
|
|
signal failed(error_message)
|
|
|
|
# Emitted when the action is manually stopped
|
|
signal stopped()
|
|
```
|
|
|
|
### Signal Connection Flow
|
|
1. CutsceneManager creates an action
|
|
2. CutsceneManager connects to the action's signals
|
|
3. When action starts, it emits `started()`
|
|
4. When action completes, it emits `completed()`
|
|
5. CutsceneManager's signal handlers update the cutscene state
|
|
|
|
```gdscript
|
|
# In CutsceneManager
|
|
func add_action(action: Action) -> void:
|
|
# Connect to action signals
|
|
action.connect("started", _on_action_started)
|
|
action.connect("completed", _on_action_completed)
|
|
action.connect("failed", _on_action_failed)
|
|
action.connect("stopped", _on_action_stopped)
|
|
|
|
# Add to queue
|
|
sequential_actions.append(action)
|
|
|
|
func _on_action_started(action: Action) -> void:
|
|
emit_signal("action_started", action)
|
|
|
|
func _on_action_completed(action: Action) -> void:
|
|
# Handle action completion
|
|
# Move to next action or complete cutscene
|
|
_handle_action_completion(action)
|
|
|
|
func _on_action_failed(action: Action, error_message: String) -> void:
|
|
# Handle action failure
|
|
print("Action failed: %s - %s" % [action.name, error_message])
|
|
# Stop the cutscene or handle error appropriately
|
|
stop()
|
|
|
|
func _on_action_stopped(action: Action) -> void:
|
|
# Handle action being stopped
|
|
_handle_action_completion(action)
|
|
```
|
|
|
|
## Parallel Action Group Completion
|
|
|
|
For parallel actions, the system tracks completion of all actions in a group:
|
|
|
|
```gdscript
|
|
# Structure for tracking parallel action groups
|
|
{
|
|
"actions": [], # Array of actions in this group
|
|
"completed_count": 0, # Number of completed actions
|
|
"total_count": 0, # Total number of actions
|
|
"on_complete": Callable # Optional callback when group completes
|
|
}
|
|
|
|
func add_parallel_actions(actions: Array, on_complete: Callable = Callable()) -> void:
|
|
# Create a parallel action group
|
|
var group = {
|
|
"actions": actions,
|
|
"completed_count": 0,
|
|
"total_count": actions.size(),
|
|
"on_complete": on_complete
|
|
}
|
|
|
|
# Connect to each action's completed signal
|
|
for action in actions:
|
|
action.connect("completed", _on_parallel_action_completed.bind(group, action))
|
|
action.connect("failed", _on_parallel_action_failed.bind(group, action))
|
|
|
|
# Add to active parallel groups
|
|
active_parallel_groups.append(group)
|
|
|
|
# Add to sequential queue as a single item
|
|
sequential_actions.append(group)
|
|
|
|
func _on_parallel_action_completed(group: Dictionary, action: Action) -> void:
|
|
# Increment completed count
|
|
group["completed_count"] += 1
|
|
|
|
# Check if all actions in group are completed
|
|
if group["completed_count"] >= group["total_count"]:
|
|
# Remove from active groups
|
|
active_parallel_groups.erase(group)
|
|
|
|
# Call optional completion callback
|
|
if group["on_complete"].is_valid():
|
|
group["on_complete"].call()
|
|
|
|
# Continue with next sequential action
|
|
_execute_next_action()
|
|
```
|
|
|
|
## Callback System
|
|
|
|
### Action-Level Callbacks
|
|
Actions can have callbacks for specific events:
|
|
|
|
```gdscript
|
|
class_name Action
|
|
extends RefCounted
|
|
|
|
# Callback properties
|
|
var on_started: Callable = Callable()
|
|
var on_completed: Callable = Callable()
|
|
var on_failed: Callable = Callable()
|
|
|
|
func start() -> void:
|
|
._set_running()
|
|
|
|
# Call started callback if valid
|
|
if on_started.is_valid():
|
|
on_started.call(self)
|
|
|
|
func _set_completed() -> void:
|
|
state = State.COMPLETED
|
|
completed.emit()
|
|
|
|
# Call completed callback if valid
|
|
if on_completed.is_valid():
|
|
on_completed.call(self)
|
|
|
|
func _set_failed(error_message: String) -> void:
|
|
state = State.FAILED
|
|
failed.emit(error_message)
|
|
|
|
# Call failed callback if valid
|
|
if on_failed.is_valid():
|
|
on_failed.call(self, error_message)
|
|
```
|
|
|
|
### Cutscene-Level Callbacks
|
|
The CutsceneManager supports callbacks for cutscene events:
|
|
|
|
```gdscript
|
|
class_name CutsceneManager
|
|
extends Node
|
|
|
|
# Cutscene callbacks
|
|
var on_started: Callable = Callable()
|
|
var on_completed: Callable = Callable()
|
|
var on_paused: Callable = Callable()
|
|
var on_resumed: Callable = Callable()
|
|
|
|
func start() -> void:
|
|
if state != State.IDLE:
|
|
return
|
|
|
|
state = State.RUNNING
|
|
is_active = true
|
|
emit_signal("cutscene_started")
|
|
|
|
# Call started callback
|
|
if on_started.is_valid():
|
|
on_started.call()
|
|
|
|
# Start first action
|
|
_execute_next_action()
|
|
|
|
func _on_cutscene_completed() -> void:
|
|
state = State.IDLE
|
|
is_active = false
|
|
emit_signal("cutscene_completed")
|
|
|
|
# Call completed callback
|
|
if on_completed.is_valid():
|
|
on_completed.call()
|
|
```
|
|
|
|
## Chaining Actions with Callbacks
|
|
|
|
Actions can be chained together using callbacks:
|
|
|
|
```gdscript
|
|
# Example: Create a sequence using callbacks
|
|
func create_chained_sequence() -> void:
|
|
var action1 = MoveAction.new(character1, Vector2(100, 100))
|
|
var action2 = DialogueAction.new(character1, "I have arrived!")
|
|
var action3 = AnimationAction.new(character1, "celebrate")
|
|
|
|
# Chain actions using callbacks
|
|
action1.on_completed = func(action):
|
|
_execute_action(action2)
|
|
|
|
action2.on_completed = func(action):
|
|
_execute_action(action3)
|
|
|
|
# Start the sequence
|
|
_execute_action(action1)
|
|
```
|
|
|
|
## Error Handling and Recovery
|
|
|
|
The system includes robust error handling:
|
|
|
|
```gdscript
|
|
# Action failure handling
|
|
func _on_action_failed(action: Action, error_message: String) -> void:
|
|
print("Action failed: %s - %s" % [action.name, error_message])
|
|
|
|
# Emit a general failure signal
|
|
emit_signal("action_failed", action, error_message)
|
|
|
|
# Decide how to handle the failure:
|
|
# 1. Stop the entire cutscene
|
|
# 2. Skip the failed action and continue
|
|
# 3. Retry the action
|
|
# 4. Execute an alternative action
|
|
|
|
# For now, we'll stop the cutscene
|
|
stop()
|
|
|
|
# Optional retry mechanism
|
|
func _retry_action(action: Action, max_retries: int = 3) -> void:
|
|
if not action.has_method("retry_count"):
|
|
action.retry_count = 0
|
|
|
|
action.retry_count += 1
|
|
|
|
if action.retry_count <= max_retries:
|
|
print("Retrying action: %s (attempt %d)" % [action.name, action.retry_count])
|
|
action.start()
|
|
else:
|
|
print("Max retries exceeded for action: %s" % action.name)
|
|
_on_action_failed(action, "Max retries exceeded")
|
|
```
|
|
|
|
## Integration with Godot's Signal System
|
|
|
|
The completion system fully integrates with Godot's signal system:
|
|
|
|
1. Uses Godot's built-in signal connection mechanisms
|
|
2. Supports both method and callable-based connections
|
|
3. Handles disconnection automatically when actions are removed
|
|
4. Provides type safety through signal parameters
|
|
|
|
```gdscript
|
|
# Example of connecting to cutscene signals from outside
|
|
func setup_cutscene() -> void:
|
|
var cutscene = CutsceneManager.new()
|
|
|
|
# Connect to cutscene signals
|
|
cutscene.connect("cutscene_started", _on_cutscene_started)
|
|
cutscene.connect("cutscene_completed", _on_cutscene_completed)
|
|
cutscene.connect("action_started", _on_action_started)
|
|
cutscene.connect("action_completed", _on_action_completed)
|
|
|
|
# Configure callbacks
|
|
cutscene.on_completed = func():
|
|
print("Cutscene finished, returning to gameplay")
|
|
# Return to gameplay state
|
|
|
|
return cutscene
|
|
```
|
|
|
|
This completion and callback system provides a flexible, robust foundation for managing action completion in cutscenes while maintaining clean separation of concerns and enabling complex action sequences. |