Files
experiment-adventure-ai/extensibility_guide.md
2025-07-30 21:52:27 -07:00

300 lines
7.9 KiB
Markdown

# Cutscene System Extensibility Guide
## Overview
This document explains how to extend the cutscene system with new action types, features, and customizations while maintaining compatibility with the existing architecture.
## Adding New Action Types
### Basic Extension Process
1. Create a new class that extends `Action`
2. Implement the required methods (`start()`, `is_completed()`)
3. Optionally implement `update()` and `stop()`
4. Add any custom properties or methods needed
### Example: Custom FadeAction
```gdscript
class_name FadeAction
extends Action
# Custom properties
var target_node: CanvasItem
var target_alpha: float
var fade_speed: float = 1.0
var start_alpha: float = 1.0
func _init(node: CanvasItem, alpha: float, speed: float = 1.0):
target_node = node
target_alpha = alpha
fade_speed = speed
name = "FadeAction"
func start() -> void:
if target_node == null:
._set_failed("Target node is null")
return
start_alpha = target_node.modulate.a
._set_running()
func update(delta: float) -> void:
if state != State.RUNNING:
return
if target_node == null:
._set_failed("Target node was destroyed")
return
# Calculate new alpha value
var current_alpha = target_node.modulate.a
var alpha_diff = target_alpha - current_alpha
var alpha_change = sign(alpha_diff) * fade_speed * delta
# Check if we've reached the target
if abs(alpha_change) >= abs(alpha_diff):
# Set final alpha and complete
var new_modulate = target_node.modulate
new_modulate.a = target_alpha
target_node.modulate = new_modulate
._set_completed()
else:
# Apply incremental change
var new_modulate = target_node.modulate
new_modulate.a += alpha_change
target_node.modulate = new_modulate
func is_completed() -> bool:
return state == State.COMPLETED
```
## Customizing the CutsceneManager
### Adding New Features
The CutsceneManager can be extended with new functionality:
```gdscript
# Extended CutsceneManager with additional features
class_name ExtendedCutsceneManager
extends CutsceneManager
# Custom properties
var skip_enabled: bool = true
var auto_skip_delay: float = 0.0
# Custom signals
signal cutscene_skipped()
# Custom methods
func skip_cutscene() -> void:
if not skip_enabled:
return
emit_signal("cutscene_skipped")
stop()
func set_auto_skip(seconds: float) -> void:
auto_skip_delay = seconds
# Implementation for auto-skip functionality
```
### Plugin Architecture
For complex extensions, consider a plugin-style architecture:
```gdscript
# Plugin interface
class_name CutscenePlugin
extends RefCounted
func initialize(manager: CutsceneManager) -> void:
# Initialize plugin with the manager
pass
func process_action(action: Action, delta: float) -> void:
# Process action each frame
pass
func cleanup() -> void:
# Clean up when cutscene ends
pass
# Example plugin for debugging
class_name DebugCutscenePlugin
extends CutscenePlugin
func process_action(action: Action, delta: float) -> void:
if action.state == Action.State.RUNNING:
print("Action %s is running" % action.name)
```
## Integration with External Systems
### Game State Integration
Connect the cutscene system to your game's state management:
```gdscript
# Integration with a game state manager
class_name GameStateIntegratedCutsceneManager
extends CutsceneManager
var game_state_manager: GameStateManager
func start() -> void:
# Notify game state manager
if game_state_manager:
game_state_manager.set_state(GameState.CUTSCENE)
# Call parent start method
super().start()
func _on_cutscene_completed() -> void:
# Notify game state manager
if game_state_manager:
game_state_manager.set_state(GameState.PLAYING)
# Call parent completion handler
super()._on_cutscene_completed()
```
### Save/Load System Integration
Add support for saving and loading cutscene states:
```gdscript
class_name SaveableCutsceneManager
extends CutsceneManager
func save_state() -> Dictionary:
return {
"current_action_index": action_index,
"sequential_actions": _serialize_actions(sequential_actions),
"parallel_groups": _serialize_parallel_groups(active_parallel_groups),
"state": state
}
func load_state(data: Dictionary) -> void:
action_index = data["current_action_index"]
sequential_actions = _deserialize_actions(data["sequential_actions"])
active_parallel_groups = _deserialize_parallel_groups(data["parallel_groups"])
state = data["state"]
```
## Performance Optimization Extensions
### Action Pooling
Implement object pooling for frequently created actions:
```gdscript
class_name PooledCutsceneManager
extends CutsceneManager
var action_pools: Dictionary = {}
func get_pooled_action(action_type: String, params: Array) -> Action:
if not action_pools.has(action_type):
action_pools[action_type] = []
var pool = action_pools[action_type]
if pool.size() > 0:
# Reuse existing action
var action = pool.pop_back()
# Reinitialize with new parameters
action.reinitialize(params)
return action
else:
# Create new action
return _create_action(action_type, params)
func return_action_to_pool(action: Action) -> void:
var action_type = action.get_class()
if not action_pools.has(action_type):
action_pools[action_type] = []
action.reset()
action_pools[action_type].append(action)
```
## Custom Action Composition
### Action Groups
Create reusable action groups for common sequences:
```gdscript
class_name ActionGroup
extends Action
var actions: Array
var current_action_index: int = 0
func _init(group_actions: Array):
actions = group_actions
name = "ActionGroup"
func start() -> void:
current_action_index = 0
if actions.size() > 0:
_execute_action(actions[0])
else:
._set_completed()
func _execute_action(action: Action) -> void:
action.connect("completed", _on_sub_action_completed.bind(action))
action.start()
func _on_sub_action_completed(action: Action) -> void:
current_action_index += 1
if current_action_index < actions.size():
_execute_action(actions[current_action_index])
else:
._set_completed()
```
## Event-Driven Extensions
### Custom Events
Add support for custom events that can trigger actions:
```gdscript
class_name EventDrivenCutsceneManager
extends CutsceneManager
var event_listeners: Dictionary = {}
func listen_for_event(event_name: String, action: Action) -> void:
if not event_listeners.has(event_name):
event_listeners[event_name] = []
event_listeners[event_name].append(action)
func trigger_event(event_name: String) -> void:
if event_listeners.has(event_name):
for action in event_listeners[event_name]:
# Add action to current sequence or execute immediately
add_action(action)
```
## Best Practices for Extensions
### 1. Maintain Compatibility
- Always call parent methods when overriding
- Keep the same method signatures
- Don't change the core behavior of existing methods
### 2. Use Composition Over Inheritance
- Prefer adding functionality through plugins or components
- Keep inheritance hierarchies shallow
- Use interfaces where possible
### 3. Provide Clear Extension Points
- Document which methods are safe to override
- Provide virtual methods for customization
- Use signals for loose coupling
### 4. Handle Errors Gracefully
- Always check for null references
- Provide meaningful error messages
- Implement fallback behaviors
### 5. Optimize for Performance
- Reuse objects when possible
- Avoid unnecessary processing
- Profile extensions for performance impact
This extensibility guide provides a framework for expanding the cutscene system while maintaining its core functionality and ease of use.