7.9 KiB
7.9 KiB
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
- Create a new class that extends
Action - Implement the required methods (
start(),is_completed()) - Optionally implement
update()andstop() - Add any custom properties or methods needed
Example: Custom FadeAction
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:
# 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:
# 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:
# 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:
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:
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:
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:
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.