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