Post

언리얼 라이라 - GameplayMessageSubsystem

라이라의 GameplayMessageSubsystem으로 느슨한 결합 메시지 브로드캐스트/수신 구조 정리

언리얼 라이라 - GameplayMessageSubsystem

GameplayMessageSubsystem

개념

UGameplayMessageSubsystemGameplayMessageRouter 플러그인에서 제공하는 UGameInstanceSubsystem이다.

송신자와 수신자가 서로를 직접 참조하지 않고, GameplayTag 채널을 통해 메시지를 주고받는 구조다.

1
2
3
4
5
송신자 → BroadcastMessage(Channel, Message)
                  ↓
       GameplayMessageSubsystem
                  ↓
수신자 ← RegisterListener(Channel, Callback)

주요 파일

1
2
3
Plugins/GameplayMessageRouter/Source/GameplayMessageRuntime/Public/GameFramework/
├── GameplayMessageSubsystem.h   ← 핵심 클래스
└── GameplayMessageTypes2.h      ← EGameplayMessageMatch, FGameplayMessageListenerParams

핵심 API

서브시스템 가져오기

1
UGameplayMessageSubsystem& MessageSystem = UGameplayMessageSubsystem::Get(this);

메시지 브로드캐스트

1
2
// 채널 태그 + USTRUCT 메시지 전송
MessageSystem.BroadcastMessage(Message.Verb, Message);

라이라 실사용 예시 (LyraHealthSet.cpp):

1
2
3
4
5
6
7
8
9
10
// 메시지 생성
FLyraVerbMessage Message;
Message.Verb       = TAG_Lyra_Damage_Message;
Message.Instigator = Data.EffectSpec.GetEffectContext().GetEffectCauser();
Message.Target     = GetOwningActor();
Message.Magnitude  = Data.EvaluatedData.Magnitude;

// 월드에 알림
UGameplayMessageSubsystem& MessageSystem = UGameplayMessageSubsystem::Get(GetWorld());
MessageSystem.BroadcastMessage(Message.Verb, Message);

리스너 등록 (멤버 함수 바인딩)

1
2
3
4
5
6
// Object의 멤버 함수로 직접 바인딩, WeakPtr로 안전하게 처리됨
ListenerHandle = MessageSubsystem.RegisterListener(
    TAG_Lyra_AddNotification_Message,
    this,
    &ThisClass::OnNotificationMessage
);

라이라 실사용 예시 (LyraAccoladeHostWidget.cpp):

1
2
3
4
5
6
// 등록
ListenerHandle = MessageSubsystem.RegisterListener(
    TAG_Lyra_AddNotification_Message, this, &ThisClass::OnNotificationMessage);

// 해제
MessageSubsystem.UnregisterListener(ListenerHandle);

리스너 등록 (람다)

1
2
3
4
5
6
7
FGameplayMessageListenerHandle Handle = MessageSystem.RegisterListener<FMyMessage>(
    MyChannel,
    [](FGameplayTag Channel, const FMyMessage& Msg)
    {
        // 처리
    }
);

리스너 해제

1
2
3
4
MessageSubsystem.UnregisterListener(Handle);

// 또는 핸들에서 직접
Handle.Unregister();

채널 매칭 방식

1
2
3
4
5
enum class EGameplayMessageMatch : uint8
{
    ExactMatch,   // "A.B" 등록 → "A.B" 만 수신 (기본값)
    PartialMatch, // "A.B" 등록 → "A.B", "A.B.C" 모두 수신
};

GameplayMessageProcessor 패턴

라이라에서 리스너를 컴포넌트 단위로 묶어 관리하는 베이스 클래스 (GameplayMessageProcessor.cpp):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void UGameplayMessageProcessor::BeginPlay()
{
    Super::BeginPlay();
    StartListening(); // 서브클래스에서 RegisterListener 호출
}

void UGameplayMessageProcessor::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
    Super::EndPlay(EndPlayReason);
    StopListening();

    UGameplayMessageSubsystem& MessageSubsystem = UGameplayMessageSubsystem::Get(this);
    for (FGameplayMessageListenerHandle& Handle : ListenerHandles)
    {
        MessageSubsystem.UnregisterListener(Handle);
    }
    ListenerHandles.Empty();
}

void UGameplayMessageProcessor::AddListenerHandle(FGameplayMessageListenerHandle&& Handle)
{
    ListenerHandles.Add(MoveTemp(Handle));
}

서브클래스에서는 StartListening()에서 등록만 하면 됨:

1
2
3
4
5
6
7
// AssistProcessor.cpp
void UAssistProcessor::StartListening()
{
    UGameplayMessageSubsystem& MessageSubsystem = UGameplayMessageSubsystem::Get(this);
    AddListenerHandle(MessageSubsystem.RegisterListener(TAG_Lyra_Elimination_Message, this, &ThisClass::OnEliminationMessage));
    AddListenerHandle(MessageSubsystem.RegisterListener(TAG_Lyra_Damage_Message,      this, &ThisClass::OnDamageMessage));
}

구조 요약

항목설명
기반 클래스UGameInstanceSubsystem (게임 인스턴스 생존)
채널 식별FGameplayTag
메시지 타입임의 USTRUCT
송신BroadcastMessage(Channel, Message)
수신 등록RegisterListener(Channel, ...)
수신 해제UnregisterListener(Handle) or Handle.Unregister()
안전성멤버 함수 바인딩 시 내부적으로 TWeakObjectPtr 사용
This post is licensed under CC BY 4.0 by the author.