Skip to content
Tuxedo CatSTUDIO
Menu

SignalBus Blueprint Usage

Review draft. This source page has not completed website editorial and release-specific verification.

SignalBus Blueprint nodes provide a simple way for Blueprints to publish and receive local messages without storing direct references to every other Actor or system.

Blueprints use the same runtime SignalBus subsystems as C++.

SignalBus does not create:

  • A separate Blueprint-only bus
  • A global message registry
  • Automatic Actor discovery
  • Automatic server-to-client messaging
  • Automatic replication

The normal flow is:

Publisher Blueprint
-> SignalBus
-> Listener Component
-> Blueprint event

Example:

Enemy Blueprint
-> publishes Combat.EnemyDefeated
-> Quest Blueprint reacts
-> UI Blueprint reacts
-> Audio Blueprint reacts

The Enemy Blueprint does not need direct references to the Quest, UI, or Audio Blueprints.


A basic Blueprint setup has four parts:

  1. Create a payload struct.
  2. Add a SignalBus Listener Component to the receiving Actor.
  3. Subscribe using the same Message ID and payload struct.
  4. Publish using the matching scope, Message ID, and payload struct.

All three routing values must match:

Scope
Message ID
Exact payload struct type

If any one of those differs, the subscriber does not receive the message.


SignalBus payloads must be reflected Unreal structs.

Create a Blueprint Structure such as:

ST_HealthChanged

Example fields:

Old Health: Float
New Health: Float
Instigator Name: Name

A typical payload should contain only the information needed to describe the event.

Good payload:

ST_DoorOpened
- Door ID
- Instigator
- Open Direction

Avoid very large payloads containing full inventories, large arrays, save-game data, or entire object state.

SignalBus works best with small event descriptions.


Use:

Make SignalBus Message Id

Provide a stable Name.

Example:

Combat.HealthChanged

Other examples:

Combat.DamageApplied
Quest.ObjectiveCompleted
UI.NotificationRequested
World.DoorOpened
Save.Completed
Travel.Started

None is invalid.

Message IDs do not need to be registered in a central list, but spelling must match exactly between publishers and subscribers.

Recommended naming style:

Category.EventName

Examples:

Combat.PlayerDied
Inventory.ItemAdded
Objective.ProgressChanged
Frontend.ScreenChanged

Avoid vague names such as:

Update
Event
Changed
Message1

Clear names make diagnostics and the Broadcast Graph easier to understand.


Add:

SignalBus Listener Component

to the Actor that should receive messages.

Typical listener Actors include:

  • HUD controllers
  • UI coordinators
  • Quest managers
  • Audio managers
  • Gameplay Actors
  • Actor Components
  • World managers
  • Session managers

The Listener Component owns the subscriptions created through it.

When the Actor ends play, the component can automatically unsubscribe those handles.

Leave:

Auto Unsubscribe On End Play

enabled unless you have a specific reason to manage cleanup manually.


Use one of these nodes:

Subscribe Signal World
Subscribe Signal Game Instance

Choose the scope based on where the message belongs.

Use World scope for messages tied to the current map, match, or gameplay World.

Examples:

Combat.DamageApplied
Enemy.Spawned
Door.Opened
Objective.ProgressChanged
Projectile.Impact

Use Game Instance scope for local session-level messages that may survive map travel.

Examples:

Profile.Loaded
Save.Completed
Login.StatusChanged
Matchmaking.SearchStarted
Travel.Started
Travel.Completed

The subscription node includes:

Payload Type Sample

Connect a value of the exact payload struct you expect.

Example:

Subscribe Signal World
Message ID: Combat.HealthChanged
Payload Type Sample: ST_HealthChanged

The sample is used only to identify the expected struct type.

The values inside the sample are not used for filtering.

SignalBus matches exact struct identity.

This means:

ST_HealthChanged

does not match:

ST_HealthChangedExtended

even when the two structs contain similar fields.


Override or implement:

On SignalBus Message Received

The callback provides:

Message ID
Payload
Scope
Subscription Handle

The callback runs only for subscriptions created by that Listener Component.

Blueprint callbacks receive an owned payload copy so the data remains safe during Blueprint execution.

The callback is not a second message and does not create duplicate routing.


Use:

Get SignalBus Payload As

Connect the output to the exact struct type used for the subscription.

Example:

On SignalBus Message Received
-> Get SignalBus Payload As ST_HealthChanged
-> Branch on Success
-> update health bar

Extraction succeeds only when the stored payload type exactly matches the requested output struct.

Always check the success result before using the extracted value.

Recommended flow:

Get SignalBus Payload As
-> Success?
True -> use payload
False -> log or ignore

A failed extraction usually means:

  • The wrong struct type was requested
  • The subscription was configured with a different payload type
  • The same Message ID is being used with multiple payload structs

Use one of these explicit nodes:

Publish Signal Immediate World
Publish Signal Queued World
Publish Signal Immediate Game Instance
Publish Signal Queued Game Instance

The payload pin accepts only reflected USTRUCT values.

You cannot publish these directly as top-level payloads:

Integer
Float
Boolean
String
Object reference
Array
Set
Map

Wrap them inside a Blueprint Structure.

Example:

Instead of publishing a Float directly:

New Health: 75.0

create:

ST_HealthChanged
- New Health: 75.0

Immediate publish is synchronous.

The subscriber callback runs before the publish node finishes.

Expected order:

Before Publish
On SignalBus Message Received
After Publish

Example scenario:

Player takes damage
-> publish Combat.HealthChanged immediately
-> health UI updates
-> damage audio reacts
-> execution returns to the publisher

Use Immediate when local listeners must react during the current execution flow.

Good uses:

  • Updating local UI immediately
  • Notifying gameplay systems of a completed state change
  • Synchronous local coordination
  • Events where callback order matters

Be careful with heavy work inside immediate subscribers because it delays the publisher.


Queued publish is deferred.

The payload is copied into the subsystem queue and the publish node returns before subscribers run.

Expected order:

Before Queue
After Queue
On SignalBus Message Received

Example scenario:

Inventory system queues Inventory.Refreshed
-> current function finishes
-> later queue pass delivers the message
-> UI and audio react

Use Queued when:

  • The publisher should finish first
  • Work can happen later
  • You want to avoid deep immediate callback chains
  • You want FIFO deferred delivery

Queued messages are delivered in order within their owning subsystem.

Queued delivery is not a timer. It does not mean “deliver in five seconds.” It means “deliver during a later SignalBus queue-processing pass.”


Publish nodes may include an optional:

Publisher

pin.

This pin is used for diagnostics and graph identity only.

It does not affect routing.

A subscriber is not selected based on the Publisher object.

SignalBus does not strongly retain the Publisher.

Example:

Publisher: Self

This helps the Inspector show which object produced the message.


ST_DoorOpened
- Door Name
- Instigator

Door Blueprint:

Open Door
-> Make ST_DoorOpened
-> Publish Signal Immediate World
Message ID: World.DoorOpened
Publisher: Self

Quest Manager Blueprint:

Begin Play
-> Subscribe Signal World
Message ID: World.DoorOpened
Payload Type Sample: ST_DoorOpened

Callback:

On SignalBus Message Received
-> Get SignalBus Payload As ST_DoorOpened
-> check Door Name
-> update quest

Why World scope?

The door belongs to the current gameplay World.


ST_SaveCompleted
- Slot Name
- Success
- Error Message

Save Manager Blueprint:

Save operation finishes
-> Publish Signal Immediate Game Instance
Message ID: Save.Completed

Frontend UI Blueprint:

Subscribe Signal Game Instance
Message ID: Save.Completed
Payload Type Sample: ST_SaveCompleted

Callback:

Get SignalBus Payload As ST_SaveCompleted
-> Success?
True -> show saved notification
False -> show error

Why Game Instance scope?

Save status belongs to the local session and may remain relevant across map travel.


Example Scenario 3: Replicated Health Change

Section titled “Example Scenario 3: Replicated Health Change”

SignalBus does not replicate messages.

For multiplayer, use Unreal networking first.

Server:

Server changes Health
-> Health replicates

Client:

OnRep_Health
-> Publish Signal Immediate World
Message ID: Combat.HealthChanged

Local client listeners:

HUD
Audio
Camera effects
Accessibility feedback

SignalBus handles local reactions after replication arrives.

Do not expect a server SignalBus publish to automatically reach client subscribers.


Use the Game Instance bus.

Publisher:

Before opening a new map
-> Publish Signal Immediate Game Instance
Message ID: Travel.Started

Subscribers:

Loading screen
Session manager
Save coordinator
Frontend flow

The Game Instance bus can survive while the old World is destroyed.

Do not queue a World message and expect it to survive into the next map.


World and Game Instance are separate buses.

This will not deliver:

Publisher:
Publish Signal Immediate World
Message ID: Save.Completed
Subscriber:
Subscribe Signal Game Instance
Message ID: Save.Completed

The Message ID may match, but the scope does not.

Likewise, each PIE client, server World, and Game Instance owns a separate SignalBus context.

SignalBus does not share messages between network peers.


SignalBus does not convert payload types.

These do not match:

Subscriber expects ST_DamageApplied
Publisher sends ST_DamageApplied_Extended

These also do not match:

Subscriber expects ST_HealthChanged
Publisher sends ST_HealthUpdated

even if the fields are identical.

Use one clearly defined struct for each message contract.

A good rule is:

One Message ID
One expected payload struct

Technically, the same Message ID can be used with multiple payload types, but doing so makes debugging and Blueprint extraction more confusing.


Subscribe nodes return a subscription handle.

Store the handle when you need manual control.

Use it to:

  • Unsubscribe one specific subscription
  • Check which callback registration is being removed
  • Manage temporary listening states

Example:

Begin Listening
-> Subscribe Signal World
-> store handle
Stop Listening
-> Unsubscribe using stored handle

Do not invent or modify handles manually.

A handle is valid only for the subsystem and subscription that created it.


Most Actor-based listeners should rely on:

Auto Unsubscribe On End Play

Manual unsubscribe is useful when:

  • A temporary mode ends
  • A UI panel closes
  • A listener should stop reacting before Actor destruction
  • The same component owns multiple subscriptions

If the component provides Unsubscribe All, it removes only handles created by that component.

It does not clear other subscribers from the bus.

Repeated unsubscribe should be treated as a no-op or failed removal, not as a new action.


Publisher:

Publish Signal World

Subscriber:

Subscribe Signal Game Instance

Result:

No delivery

Fix: use the same scope.


Publisher:

Combat.HealthChanged

Subscriber:

Combat.HealthChange

Result:

No delivery

Fix: use the exact same Name.


Publisher sends:

ST_HealthChanged

Subscriber expects:

ST_DamageApplied

Result:

No delivery

Fix: use the exact same reflected struct.


Adding a Listener Component alone does not create a subscription.

You must call:

Subscribe Signal World

or:

Subscribe Signal Game Instance

Calling Subscribe every Tick or every time a widget refreshes creates duplicate subscriptions.

Result:

One publish
-> callback runs multiple times

Subscribe once during an appropriate lifecycle event, such as:

Begin Play
On Initialized
On Activated

Unsubscribe when that listening period ends.


Publishing every frame can create:

  • High callback volume
  • Large diagnostic history
  • Warning activity
  • Queue backlog
  • Editor slowdown

Publish only when meaningful state changes.

Bad:

Tick
-> Publish HealthChanged every frame

Better:

Health value actually changes
-> Publish HealthChanged once

Queued does not mean delayed by a specific number of seconds.

Use Unreal timers for scheduled behavior.

Set Timer
-> timer expires
-> publish SignalBus message

8. Expecting old messages after subscribing

Section titled “8. Expecting old messages after subscribing”

SignalBus does not replay history.

Message published
Subscriber registers later

The new subscriber does not receive the old message.

Read current state first, then subscribe for future changes.


A client-local publish does not reach the server.

A server-local publish does not reach clients.

Use:

  • Server RPCs
  • Client RPCs
  • NetMulticast RPCs
  • Replicated properties

Then publish locally after the network event arrives.


Blueprint listeners copy payloads for safety.

Large arrays, maps, strings, and nested structs increase copy cost.

Prefer compact payloads containing:

  • IDs
  • Names
  • Small numeric values
  • Small state summaries
  • Weak or soft identity information where appropriate

Let subscribers query large durable state from its owning system.


Immediate callbacks run before the publisher continues.

A slow subscriber delays every immediate publisher using that message.

Use queued delivery when work does not need to complete immediately.


The optional Publisher object is diagnostic context only.

Subscribers do not filter automatically by Publisher.

If filtering is required, include an identifier in the payload and check it in the subscriber.

Example:

ST_DoorOpened
- Door ID

Subscriber:

if Door ID == Expected Door
-> react

Begin Play
-> Subscribe
End Play
-> automatic unsubscribe
Widget Activated
-> Subscribe
-> store handle
Widget Deactivated
-> Unsubscribe
Game Instance-managed object initializes
-> Subscribe Signal Game Instance
Object shuts down
-> Unsubscribe

Avoid subscribing in construction scripts.

Construction scripts may run in editor contexts and multiple times.


Blueprint publishes and subscriptions appear in the same runtime diagnostics used by C++.

Use the SignalBus Runtime Inspector to review:

  • Active subscriptions
  • Message history
  • World and Game Instance contexts
  • Queued messages
  • Payload snapshots
  • Warning entries
  • Publisher and subscriber identities
  • Broadcast Graph topology

Useful checks when a message is not received:

  1. Is the subscriber active?
  2. Do scopes match?
  3. Do Message IDs match exactly?
  4. Do payload structs match exactly?
  5. Is the correct PIE context selected?
  6. Was the message Immediate or Queued?
  7. Was the listener destroyed or unsubscribed?
  8. Did queued processing occur?
  9. Was queue capacity exceeded?
  10. Did Get SignalBus Payload As succeed?

Payload inspection captures the routed payload once. It does not create a separate snapshot for every Blueprint subscriber.


Prefer:

  • Small payload structs
  • Event-based publishing
  • One subscription per intended listener
  • Queued delivery for non-urgent work
  • Immediate delivery only when synchronous behavior is required
  • Stable Message IDs
  • Automatic cleanup

Avoid:

  • Publishing every Tick
  • Very large containers
  • Deep nested payload structs
  • Repeated duplicate subscription
  • Long-running immediate callbacks
  • Using SignalBus as bulk storage
  • Using SignalBus instead of replication

For every Blueprint message flow, confirm:

Publisher scope matches subscriber scope
Message ID matches exactly
Payload struct matches exactly
Listener Component exists
Subscribe node has executed
Payload extraction checks Success
Subscription is cleaned up
Immediate or Queued mode is intentional
Network behavior is handled separately

The simplest reliable pattern is:

Create a small payload struct
Use one stable Message ID
Subscribe once
Publish only when something changes
Extract the exact payload type
Let the Listener Component clean up