GameplayMessageSystem
Overview of the GameplayMessageSystem
The GameplayMessageSystem (also known as the GameplayMessageSubsystem, or GameplayMessageRouter) is a subsystem that allows otherwise unconnected gameplay objects to communicate with each other.
Table of Contents
- About the GameplayMessageSystem
- Enabling the GameplayMessageSystem
- Dependencies of the GameplayMessageSubsystem
- Up
- Using the GameplayMessageSystem
- Using the GameplayMessageSystem in C++
- Using the GameplayMessageSystem in Blueprint
- GameplayMessageSubsystem in Lyra
About the GameplayMessageSystem
Introduced with Unreal Engine 5.0, the GameplayMessageSystem provides a standalone and easily-extensible messaging system. Using this system gameplay objects can communicate with each other without needed to find direct references, assign delegates, or otherwise know about each other. Properly used, the GameplayMessageSystem can dramatically reduce interdependency between objects in your project.
Core Concepts
Broadcast
- A gameplay object (typically an Actor) can Broadcast using the GameplayMessageSystem
- The Broadcast will specify a Channel and a Message (filled out by the sender prior to broadcasting)
- Broadcasters do not know which objects will receive their messages (if any)
- Broadcasts are not network replicated unless explicitly replicated via RPC
Listener
- A gameplay object (typically an Actor) can Listen using the GameplayMessageSystem
- The Listener will specify a Channel to listen to, and a Callback function
- The Callback function must have parameters for the Channel and Message
- Listeners do not know which objects are Broadcasting messages (if any)
Channel
- A Channel is a GameplayTag.
- Both the Broadcaster and Listener must agree on a Channel if a message is to be received
- You can arbitrarily define any GameplayTag as a Channel
- To prevent future confusion, you might want to devise an organizational hierarchy for message channels
Message
- A Message is a Structure[1]
- Any USTRUCT is valid to use as a message, but both Broadcaster and Listener must agree on the type of structure used
- Typically the Listener will utilize the data contained in the Message to perform an action
The Lyra example project uses a single FLyraVerbMessage
structure for almost all Messages sent. This Message structure is shown below, and has enough information data fields to encompass most common gameplay events.
USTRUCT(BlueprintType)
struct FLyraVerbMessage
{
GENERATED_BODY()
UPROPERTY(BlueprintReadWrite, Category=Gameplay)
FGameplayTag Verb;
UPROPERTY(BlueprintReadWrite, Category=Gameplay)
TObjectPtr<UObject> Instigator = nullptr;
UPROPERTY(BlueprintReadWrite, Category=Gameplay)
TObjectPtr<UObject> Target = nullptr;
UPROPERTY(BlueprintReadWrite, Category=Gameplay)
FGameplayTagContainer InstigatorTags;
UPROPERTY(BlueprintReadWrite, Category=Gameplay)
FGameplayTagContainer TargetTags;
UPROPERTY(BlueprintReadWrite, Category=Gameplay)
FGameplayTagContainer ContextTags;
UPROPERTY(BlueprintReadWrite, Category=Gameplay)
double Magnitude = 1.0;
// Returns a debug string representation of this message
LYRAGAME_API FString ToString() const;
};
Publish-Subscribe Model
If you are familiar with other Software Engineering patterns, you may recognize that the GameplayMessageSystem is an implementation of a Publish-Subscribe pattern (or pub-sub). For more information about this Software Design Pattern read here.
Why Use the GameplayMessageSystem
Benefits
- Dramatically reduce interdependency between objects in your project
- Eliminate linkage between User Interface and gameplay actors
Drawbacks
- Decoupling objects can make it more challenging to know what might be triggering specific behaviors
- Challenging to cleanly organize all of the GameplayTags required for the GameplayMessagingSystem