Godot Signals
by Aendawyn
10
A powerful signal system for Godot Engine that provides efficient signal handling with support for filtering, mapping, centralized event management, and high-performance signal processing.
๐ฃ๏ธ Roadmap
Here are some planned features and improvements:
- ๐ Broker Binding: Possibility to bind to the broker and receive a GSignal instance
- โก Extended Operations: Add more operations to GSignal such as debounce, merge, and more
- ๐ Suggest a feature by opening an issue on GitHub!
โจ Features
- ๐ Signal Processing: Create signal processing chains with filter and map operations
- ๐ก Signal Broker: Connect signals across your game without direct object references
- โก Smart Connection Types: Automatically optimizes for high or low frequency signals
- ๐ Pattern Matching: Subscribe to signals using wildcard patterns
- ๐ท๏ธ Alias System: Identify objects by name, groups, or custom aliases
- ๐ Optimized Performance: Reduced overhead and minimal allocations for efficient processing
๐ Quick Start
๐ Signal Processing
Create powerful signal processing chains with just a few lines of code:
# Filter, transform, and react to signals
GSignals.from(damage_signal)
.filter(func(amount: int) -> bool: return amount >= 10)
.map(func(amount: int) -> int: return amount * critical_multiplier)
.bind(func(final_damage: int): apply_damage(final_damage))
๐ก Signal Broker
Connect components without direct references:
# Broadcasting side: Register player node's signals with the broker
GBroker.broadcast_signals_of(player_node, "player")
# Listening side: Subscribe to player damage events anywhere in your code
GBroker.subscribe("player:damage_taken", func(amount): update_health_ui(amount))
๐ฅ Installation
- Download or clone this repository
- Copy the
addons/godot-signals
folder into your project'saddons
directory - Enable the plugin in Project Settings > Plugins
๐ Tutorial
๐ Signal Processing
๐ Basic Signal Connection
# Connect to a signal with a simple callback
GSignals.from(player.health_changed)
.bind(func(new_health): update_health_bar(new_health))
๐ Filtering Signals
# Only process signals where the value meets certain criteria
GSignals.from(enemy.attack)
.filter(func(damage: int) -> bool: return damage > 5)
.bind(func(damage): play_heavy_hit_sound())
๐ Transforming Signals
# Transform signal data before processing
GSignals.from(position_changed)
.map(func(pos: Vector2) -> float: return pos.distance_to(Vector2.ZERO))
.bind(func(distance: float): set_volume(100 - distance))
โฑ๏ธ Delaying Signals
# Delay signal processing by a specific time
GSignals.from(damage_taken)
.delay(0.5) # Delay by 0.5 seconds
.bind(func(amount): play_delayed_damage_effect(amount))
๐ Debouncing Signals
# Debounce rapid signal emissions
GSignals.from(mouse_moved)
.debounce(0.1) # Only process after 0.1s of inactivity
.bind(func(position): update_hover_effect(position))
๐๏ธ Connection Management
# Store and manage connections
var connection = GSignals.from(timer.timeout).bind(func(): spawn_enemy())
# Temporarily disable connection
connection.stop()
# Re-enable connection
connection.start()
๐ก Signal Broker
๐ฃ Broadcasting Signals
# Register an object's signals with the broker
GBroker.broadcast_signals_of(
player, # The object whose signals will be broadcasted
"player", # Alias for identifying the object (optional)
GBroker.GBrokerBroadcastFlags.SCRIPT_ONLY # Which signals to broadcast
)
๐ท๏ธ Setting Multiple Aliases
# Register with multiple aliases for more flexible subscription patterns
GBroker.broadcast_signals_of(
boss_entity,
["boss", "enemy", "entity"]
)
๐ Subscribing to Signals
# Subscribe to specific signals
GBroker.subscribe("player:health_changed", func(new_health): update_health_ui(new_health))
# Use wildcard patterns to subscribe to multiple signals
GBroker.subscribe("player:*", func(emitter, signal_name, args): print("Player signal: ", signal_name))
GBroker.subscribe("*:damage_taken", func(damage): update_global_damage_counter(damage))
๐งน Cleanup
# Clear all subscriptions and signal handlers when switching scenes
func _exit_tree():
GBroker.reset()
๐ฌ Advanced Broker Usage
๐ Callback Arguments Handling
The broker intelligently handles callback arguments depending on how many parameters your callback function accepts:
# Just receive the signal arguments (most common)
GBroker.subscribe("player:health_changed", func(health_amount): update_ui(health_amount))
# Receive emitter object, signal name, and arguments
GBroker.subscribe("player:*", func(emitter, signal_name, args):
print("Signal %s from %s with args %s" % [signal_name, emitter.name, args])
)
# Mixed patterns with different argument counts
GBroker.subscribe("enemy:hit", func(damage, hit_position):
spawn_particle(hit_position)
apply_damage(damage)
)
# Missing arguments are filled with null
GBroker.subscribe("*:*", func(emitter, signal_name, arg1, arg2, arg3):
# arg2 and arg3 will be null if the signal emits less than 3 arguments
print("%s emitted %s with up to 3 args: %s, %s, %s" % [
emitter.name, signal_name, arg1, arg2, arg3
])
)
๐ Notes on argument handling:
- If the callback has fewer parameters than the signal provides, extra signal arguments are ignored
- If the callback has more parameters than the signal provides, extra callback parameters receive null
- The first callback parameter can receive the emitter object if an extra parameter is available
- The second parameter can receive the signal name if additional parameters are available
- Subsequent parameters receive the signal arguments
๐ง Advanced Features
โก Custom Signal Frequency Optimization
# Optimize for high-frequency signals (e.g., position updates)
GSignals.from(position_changed, GSignals.GSignalsBindFlags.HIGH_FREQUENCY_HINT)
๐ค Automatic Alias Detection
When no alias is provided, the broker automatically uses:
- ๐ฅ The node's groups (except internal groups)
- ๐ The node's name
- ๐ค Snake case version of the node's name
# For a node named "PlayerCharacter" in group "characters"
GBroker.broadcast_signals_of(player_node)
# This will automatically register with aliases:
# - "characters" (from group)
# - "PlayerCharacter" (from name)
# - "player_character" (snake case conversion)
โ๏ธ Performance Considerations
- ๐ Chain operations carefully as each adds processing overhead
- ๐๏ธ Use the
HIGH_FREQUENCY_HINT
flag for signals that emit multiple times per frame - ๐ง The pattern matching system uses caching to optimize frequent signal matches
- ๐ Direct signal connections with GSignals are more efficient than broker subscriptions when you have direct references
๐ License
This project is licensed under the MIT License - see the LICENSE file for details.
Download
Version0.1.0
Download Now
Support
If you need help or have questions about this plugin, please contact the author.
Contact Author