Skip to content

SignalBus API

The main SignalBus class provides the core functionality for signal management and communication.

Static Methods

get_singleton() -> SignalBus*

Returns the global SignalBus singleton instance.

gdscript
var bus = SignalBus.get_singleton()

Returns: SignalBus singleton instance or null if not initialized

Signal Operations

emit(signal_name: String, args: Array) -> void

Emits a signal on the default channel with the given arguments.

Parameters:

  • signal_name (String): Name of the signal to emit
  • args (Array): Array of arguments to pass to subscribers
gdscript
bus.emit("player_died", [])
bus.emit("item_collected", ["sword", 100])
bus.emit("level_completed", ["level_1", 1500])

emit_on_channel(channel: String, signal_name: String, args: Array) -> void

Emits a signal on a specific channel.

Parameters:

  • channel (String): Channel name to emit on
  • signal_name (String): Name of the signal to emit
  • args (Array): Array of arguments to pass to subscribers
gdscript
bus.emit_on_channel("ui", "show_notification", ["Level Up!"])
bus.emit_on_channel("audio", "play_sound", ["level_up"])
bus.emit_on_channel("gameplay", "enemy_spawned", ["goblin", Vector2(100, 50)])

Subscription Management

subscribe(signal_name: String, callable: Callable) -> int

Subscribes to a signal on the default channel.

Parameters:

  • signal_name (String): Name of the signal to subscribe to
  • callable (Callable): Function to call when signal is emitted

Returns: Subscription ID (int) for unsubscribing, or -1 on error

gdscript
var sub_id = bus.subscribe("player_died", _on_player_died)
var sub_id2 = bus.subscribe("item_collected", _on_item_collected)

func _on_player_died():
    print("Player died!")

func _on_item_collected(item_type, value):
    print("Collected item: %s, value: %d" % [item_type, value])

subscribe_on_channel(channel: String, signal_name: String, callable: Callable) -> int

Subscribes to a signal on a specific channel.

Parameters:

  • channel (String): Channel name to subscribe on
  • signal_name (String): Name of the signal to subscribe to
  • callable (Callable): Function to call when signal is emitted

Returns: Subscription ID (int) for unsubscribing, or -1 on error

gdscript
var sub_id = bus.subscribe_on_channel("ui", "button_clicked", _on_ui_button)
var sub_id2 = bus.subscribe_on_channel("audio", "sound_played", _on_audio_sound)

func _on_ui_button(button_id):
    print("UI button clicked: %d" % button_id)

func _on_audio_sound(sound_name):
    play_sound(sound_name)

unsubscribe(subscription_id: int) -> bool

Removes a specific subscription by ID.

Parameters:

  • subscription_id (int): ID returned by subscribe() or subscribe_on_channel()

Returns: true if subscription was found and removed, false otherwise

gdscript
var sub_id = bus.subscribe("test_signal", _on_test)
# Later...
var success = bus.unsubscribe(sub_id)
if success:
    print("Successfully unsubscribed")

unsubscribe_all(subscriber: Object) -> void

Removes all subscriptions for a specific object.

Parameters:

  • subscriber (Object): Object whose subscriptions should be removed
gdscript
func _exit_tree():
    bus.unsubscribe_all(self)  # Remove all subscriptions from this node

Instance Signals

get_instance_signal(object: Object, base_signal: String) -> String

Generates a unique signal name for an object instance.

Parameters:

  • object (Object): Object instance to generate signal for
  • base_signal (String): Base signal name

Returns: Unique signal name (String) in format "base_signal_instanceId"

gdscript
var unique_signal = bus.get_instance_signal(enemy, "on_damage")
# Returns something like "on_damage_123456"

subscribe_instance(object: Object, base_signal: String, callable: Callable) -> int

Subscribes to an instance-specific signal.

Parameters:

  • object (Object): Object instance to subscribe for
  • base_signal (String): Base signal name
  • callable (Callable): Function to call when signal is emitted

Returns: Subscription ID (int) for unsubscribing, or -1 on error

gdscript
# Each enemy subscribes to its own damage signal
bus.subscribe_instance(enemy1, "on_damage", _on_enemy1_damage)
bus.subscribe_instance(enemy2, "on_damage", _on_enemy2_damage)

emit_instance(object: Object, base_signal: String, args: Array) -> void

Emits a signal to a specific object instance.

Parameters:

  • object (Object): Object instance to emit signal for
  • base_signal (String): Base signal name
  • args (Array): Array of arguments to pass to subscribers
gdscript
# Deal damage to specific enemy
bus.emit_instance(target_enemy, "on_damage", [25])

Signal Filtering

subscribe_filtered(signal_name: String, filter: Callable, callable: Callable) -> int

Subscribes to a signal with a filter condition.

Parameters:

  • signal_name (String): Name of the signal to subscribe to
  • filter (Callable): Function that returns true if signal should be processed
  • callable (Callable): Function to call when signal passes filter

Returns: Subscription ID (int) for unsubscribing, or -1 on error

gdscript
# Only receive valuable items (value > 50)
var sub_id = bus.subscribe_filtered(
    "item_collected",
    func(item_type, value): return value > 50,
    _on_valuable_item
)

func _on_valuable_item(item_type, value):
    print("Valuable item collected: %s, value: %d" % [item_type, value])

subscribe_filtered_on_channel(channel: String, signal_name: String, filter: Callable, callable: Callable) -> int

Subscribes to a filtered signal on a specific channel.

Parameters:

  • channel (String): Channel name to subscribe on
  • signal_name (String): Name of the signal to subscribe to
  • filter (Callable): Function that returns true if signal should be processed
  • callable (Callable): Function to call when signal passes filter

Returns: Subscription ID (int) for unsubscribing, or -1 on error

gdscript
# Only receive boss damage events
var sub_id = bus.subscribe_filtered_on_channel(
    "gameplay",
    "enemy_damaged",
    func(enemy_type, damage): return enemy_type == "boss",
    _on_boss_damaged
)

Async Operations

subscribe_async(signal_name: String, callback: Callable) -> int

Subscribes to an async signal.

Parameters:

  • signal_name (String): Name of the async signal to subscribe to
  • callback (Callable): Function to execute in background thread

Returns: Subscription ID (int) for unsubscribing, or -1 on error

gdscript
var sub_id = bus.subscribe_async("save_game", func(data):
    # This runs in background thread
    save_to_file(data)
)

subscribe_async_with_callbacks(signal_name: String, callback: Callable, on_complete: Callable, on_error: Callable) -> int

Subscribes to an async signal with completion and error callbacks.

Parameters:

  • signal_name (String): Name of the async signal to subscribe to
  • callback (Callable): Function to execute in background thread
  • on_complete (Callable): Function to call on main thread when callback completes
  • on_error (Callable): Function to call on main thread when callback fails

Returns: Subscription ID (int) for unsubscribing, or -1 on error

gdscript
var sub_id = bus.subscribe_async_with_callbacks(
    "load_level",
    func(level_name):  # Background
        return load_level_data(level_name)
    ,
    func(level_data):  # Main thread on success
        current_level = level_data
        show_level_loaded()
    ,
    func(error):  # Main thread on error
        show_error("Failed to load level: " + error)
)

emit_async(signal_name: String, args: Array) -> Array

Emits an async signal.

Parameters:

  • signal_name (String): Name of the async signal to emit
  • args (Array): Array of arguments to pass to subscribers

Returns: Array of task IDs (Array) for the created async tasks

gdscript
var task_ids = bus.emit_async("save_game", [game_data])
print("Created %d async tasks" % task_ids.size())

unsubscribe_async(subscription_id: int) -> bool

Removes an async subscription.

Parameters:

  • subscription_id (int): ID returned by async subscription methods

Returns: true if subscription was found and removed, false otherwise

gdscript
var sub_id = bus.subscribe_async("save_game", save_callback)
var success = bus.unsubscribe_async(sub_id)

process_async_tasks() -> void

Processes completed async tasks. Must be called regularly from main thread.

gdscript
func _process(_delta):
    bus.process_async_tasks()  # Handle completed async tasks

get_active_async_task_count() -> int

Returns the number of currently active async tasks.

Returns: Number of active tasks (int)

gdscript
var active_count = bus.get_active_async_task_count()
print("Active tasks: %d" % active_count)

wait_all_async_tasks() -> void

Waits for all async tasks to complete. Blocks until all tasks are done.

gdscript
func _exit_tree():
    bus.wait_all_async_tasks()  # Wait for saves to complete

cancel_async_task(task_id: int) -> bool

Cancels an async task.

Parameters:

  • task_id (int): ID of the task to cancel

Returns: true if task was cancelled, false otherwise

gdscript
var task_ids = bus.emit_async("heavy_operation", [data])
for task_id in task_ids:
    if bus.cancel_async_task(task_id):
        print("Cancelled task: %d" % task_id)

Debug and Monitoring

set_debug_enabled(enabled: bool) -> void

Enables or disables debug logging.

Parameters:

  • enabled (bool): true to enable debug mode, false to disable
gdscript
if OS.is_debug_build():
    bus.set_debug_enabled(true)

get_signal_list() -> Array[Dictionary]

Returns information about all registered signals.

Returns: Array of dictionaries with signal information:

  • name (String): Signal name
  • channel (String): Channel name
  • subscriber_count (int): Number of subscribers
gdscript
var signals = bus.get_signal_list()
for signal_info in signals:
    print("Signal: %s, Channel: %s, Subscribers: %d" % [
        signal_info.name, signal_info.channel, signal_info.subscriber_count
    ])

get_channel_list() -> PackedStringArray

Returns a list of all active channels.

Returns: Array of channel names (PackedStringArray)

gdscript
var channels = bus.get_channel_list()
print("Active channels: ", channels)

get_subscriber_count(signal_name: String) -> int

Returns the total number of subscribers for a signal across all channels.

Parameters:

  • signal_name (String): Name of the signal to count subscribers for

Returns: Number of subscribers (int)

gdscript
var count = bus.get_subscriber_count("player_died")
print("Subscribers: %d" % count)

get_subscriber_count_on_channel(channel: String, signal_name: String) -> int

Returns the number of subscribers for a signal on a specific channel.

Parameters:

  • channel (String): Channel name to check
  • signal_name (String): Signal name to count subscribers for

Returns: Number of subscribers (int)

gdscript
var count = bus.get_subscriber_count_on_channel("ui", "button_clicked")
print("UI button subscribers: %d" % count)

get_total_signals_emitted() -> int

Returns the total number of signals emitted since startup.

Returns: Total signal count (int)

gdscript
var total = bus.get_total_signals_emitted()
print("Total signals emitted: %d" % total)

Signal Bridging

bridge_signal(node: Node, signal_name: String) -> int64_t

Bridges a node signal to the default channel.

Parameters:

  • node (Node): Node object to bridge from
  • signal_name (String): Signal name to bridge

Returns: Bridge ID (int64_t) for managing the bridge, or -1 on error

gdscript
var bridge_id = bus.bridge_signal($Button, "pressed")
var timer_bridge = bus.bridge_signal($GameTimer, "timeout")

bridge_signal_to_channel(node: Node, signal_name: String, channel: String) -> int64_t

Bridges a node signal to a specific channel.

Parameters:

  • node (Node): Node object to bridge from
  • signal_name (String): Signal name to bridge
  • channel (String): Target channel name

Returns: Bridge ID (int64_t) for managing the bridge, or -1 on error

gdscript
var bridge_id = bus.bridge_signal_to_channel($SettingsButton, "pressed", "ui")
var enemy_bridge = bus.bridge_signal_to_channel($Enemy, "died", "gameplay")

relay_node_signal(node: Node, node_signal: String, bus_signal: String) -> int64_t

Relays a node signal with a different bus signal name.

Parameters:

  • node (Node): Node object to relay from
  • node_signal (String): Node signal name
  • bus_signal (String): Bus signal name to emit

Returns: Bridge ID (int64_t) for managing the bridge, or -1 on error

gdscript
var bridge_id = bus.relay_node_signal($StartButton, "pressed", "menu_action")
var health_bridge = bus.relay_node_signal($Player, "health_changed", "player_health_update")

relay_node_signal_to_channel(node: Node, node_signal: String, bus_signal: String, channel: String) -> int64_t

Relays a node signal to a specific channel with a different bus signal name.

Parameters:

  • node (Node): Node object to relay from
  • node_signal (String): Node signal name
  • bus_signal (String): Bus signal name to emit
  • channel (String): Target channel name

Returns: Bridge ID (int64_t) for managing the bridge, or -1 on error

gdscript
var bridge_id = bus.relay_node_signal_to_channel(
    $QuitButton,
    "pressed",
    "quit_requested",
    "ui"
)

unbridge_signal(bridge_id: int64_t) -> bool

Removes a signal bridge.

Parameters:

  • bridge_id (int64_t): Bridge ID returned by bridge methods

Returns: true if bridge was found and removed, false otherwise

gdscript
var bridge_id = bus.bridge_signal($Button, "pressed")
# Later...
var success = bus.unbridge_signal(bridge_id)
if success:
    print("Bridge removed successfully")

cleanup_invalid_bridges() -> int32_t

Cleans up invalid bridges (nodes that have been freed).

Returns: Number of bridges cleaned up (int32_t)

gdscript
var cleaned = bus.cleanup_invalid_bridges()
print("Cleaned up ", cleaned, " invalid bridges")

Channel Management

clear_channel(channel: String) -> void

Removes all subscriptions from a specific channel.

Parameters:

  • channel (String): Channel name to clear
gdscript
bus.clear_channel("ui")  # Remove all UI channel subscriptions

clear_all() -> void

Removes all subscriptions from all channels.

gdscript
bus.clear_all()  # Remove all subscriptions everywhere

Error Handling

Most methods return error codes or boolean values to indicate success/failure:

  • Subscription methods: Return -1 on error, positive ID on success
  • Unsubscribe methods: Return true on success, false on failure
  • Debug mode: Check bus.set_debug_enabled(true) for detailed error logging

Thread Safety

  • All synchronous methods must be called from the main thread
  • Async callbacks execute in background threads
  • Completion/error callbacks execute on main thread
  • Never access scene tree in async callbacks

Released under the MIT License