initial
This commit is contained in:
275
action_completion_system.md
Normal file
275
action_completion_system.md
Normal file
@@ -0,0 +1,275 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user