Event Dispatcher
옵저버 패턴 기반의 이벤트 디스패처로 블루프린트 간 느슨한 결합을 구현합니다
이벤트 디스패처란
옵저버 패턴과 이벤트 디스패처의 핵심 개념
이벤트 디스패처(Event Dispatcher)는 하나의 블루프린트에서 이벤트를 발생(Broadcast)시키면, 해당 이벤트에 구독(Bind)한 다른 블루프린트들이 알림을 받는 구조입니다. 이는 소프트웨어 디자인 패턴 중 옵저버 패턴(Observer Pattern)의 구현입니다.
이벤트 소유자
이벤트 발생
Bind된 함수 실행
Bind된 함수 실행
왜 이벤트 디스패처를 사용하는가?
느슨한 결합
Publisher는 누가 구독하는지 알 필요가 없습니다. 새 구독자를 추가해도 Publisher 코드를 수정할 필요가 없습니다.
1:N 통신
하나의 이벤트에 여러 구독자가 바인딩 가능합니다. 체력 변경 시 HUD, 사운드, VFX가 동시에 반응할 수 있습니다.
모듈화
각 시스템이 독립적으로 동작하면서 필요할 때만 통신합니다. 유지보수와 확장이 쉬워집니다.
디스패처 생성과 호출
이벤트 디스패처의 생성, 시그니처 정의, 호출 방법
생성 방법
My Blueprint 패널에서 Event Dispatchers 옆의 + 버튼을 클릭합니다.
// BP_Player에 이벤트 디스패처 생성
BP_Player
└─ Event Dispatchers
└─ OnHealthChanged
├─ Input: NewHealth (Float)
├─ Input: MaxHealth (Float)
└─ Input: DamageCauser (Actor Reference)
// Details 패널 > Inputs 에서 파라미터 추가
호출(Call / Broadcast)
디스패처를 이벤트 그래프에 드래그하면 3가지 옵션이 표시됩니다:
| 옵션 | 설명 |
|---|---|
| Call | 이벤트를 발생시킵니다. 바인딩된 모든 함수/이벤트가 실행됩니다. |
| Bind | 이벤트에 함수/이벤트를 구독합니다. |
| Unbind | 구독을 해제합니다. |
| Unbind All | 모든 구독을 해제합니다. |
| Event | 바인딩용 이벤트 노드를 생성합니다 (자기 자신 블루프린트 내에서 사용). |
| Assign | Bind + Event를 한 번에 생성합니다. |
// BP_Player 내부 - 데미지를 받았을 때 디스패처 호출
[Event AnyDamage]
├─ Damage: Float
└─ DamageCauser: Actor
│
├──> [Set Health] (Health - Damage)
│
└──> [Call OnHealthChanged]
├─ NewHealth: Health
├─ MaxHealth: MaxHealth
└─ DamageCauser: DamageCauser
바인딩 패턴
외부 블루프린트에서 이벤트 디스패처에 구독하기
외부 블루프린트에서 바인딩
다른 블루프린트(예: HUD, 적 AI)에서 BP_Player의 이벤트 디스패처에 바인딩하려면 먼저 BP_Player의 참조를 얻어야 합니다.
// WBP_HealthBar에서 BP_Player의 OnHealthChanged에 바인딩
[Event Construct]
│
├──> [Get Owning Player Pawn] ──> [Cast To BP_Player]
│ │
│ └─ As BP Player ──> [Bind Event to OnHealthChanged]
│ └─ Event: [Custom Event: UpdateHealthBar]
│
└──> (기타 초기화)
// Custom Event: UpdateHealthBar
[UpdateHealthBar]
├─ NewHealth (Float)
├─ MaxHealth (Float)
└─ DamageCauser (Actor)
│
└──> [Set Percent] (ProgressBar)
└─ Percent: NewHealth / MaxHealth
Assign 단축키
Actor 참조에서 드래그하여 Assign OnHealthChanged를 선택하면, Bind 노드와 Custom Event가 한 번에 생성됩니다. 가장 빠른 바인딩 방법입니다.
Unbind (구독 해제)
// 위젯이 제거될 때 바인딩 해제
[Event Destruct] ──> [Unbind Event from OnHealthChanged]
// 또는 모든 바인딩 한번에 해제
[Event Destruct] ──> [Unbind All Events from OnHealthChanged]
바인딩한 오브젝트가 파괴될 때 반드시 Unbind하세요. 그렇지 않으면 가비지 컬렉터가 해당 오브젝트를 회수하지 못하는 순환 참조가 발생할 수 있습니다. 특히 위젯과 Actor 간의 바인딩에서 흔히 발생합니다. Event Destruct(위젯) 또는 Event EndPlay(Actor)에서 Unbind를 수행하세요.
실전 활용 예제
게임에서 자주 사용되는 이벤트 디스패처 패턴
패턴 1: 사망 이벤트 체인
// BP_Character에 OnDeath 디스패처 정의
OnDeath (DeadActor: Actor, Killer: Actor)
// 구독자들:
BP_GameMode ──Bind──> OnPlayerDeath() → 리스폰 타이머 시작
WBP_DeathScreen──Bind──> ShowDeathUI() → 사망 화면 표시
BP_MusicManager──Bind──> PlayDeathMusic() → BGM 변경
BP_DropSystem ──Bind──> SpawnDrops() → 아이템 드롭
패턴 2: 인벤토리 변경 알림
// BP_InventoryComponent에 정의
OnInventoryUpdated (SlotIndex: Integer, NewItem: S_ItemData, OldItem: S_ItemData)
// BP_InventoryComponent 내부:
[AddItem] ──> (배열에 추가) ──> [Call OnInventoryUpdated]
[RemoveItem] ──> (배열에서 제거) ──> [Call OnInventoryUpdated]
// WBP_Inventory(UI)에서 바인딩:
[Bind Event to OnInventoryUpdated] ──> [Refresh Slot UI]
패턴 3: 퀘스트 진행 상황
// BP_QuestManager에 정의
OnQuestProgressUpdated (QuestID: Name, CurrentCount: Integer, RequiredCount: Integer)
OnQuestCompleted (QuestID: Name)
// 여러 시스템이 동일한 디스패처를 구독:
WBP_QuestTracker → UI 갱신
BP_AchievementSys → 도전과제 확인
BP_AudioManager → 완료 사운드 재생
BP_NotificationSys → 알림 표시
이벤트 디스패처 네이밍: On 접두사를 사용하세요 (OnHealthChanged, OnDeath, OnItemCollected).
파라미터: 구독자가 필요로 하는 최소한의 정보만 포함하세요.
호출 시점: 상태 변경이 완료된 후에 Call하세요 (값 설정 → 디스패처 Call).
핵심 요약
- 이벤트 디스패처는 옵저버 패턴을 구현하여 블루프린트 간 느슨한 결합(loose coupling)을 가능하게 한다
- Call로 이벤트를 발생시키고, Bind로 구독, Unbind로 해제한다
- Assign 옵션으로 Bind + Custom Event를 한 번에 생성할 수 있다
- 파괴되는 오브젝트에서 반드시 Unbind하여 순환 참조를 방지한다
- 체력 변경, 사망, 인벤토리 갱신, 퀘스트 진행 등 1:N 알림에 이벤트 디스패처가 적합하다
- 디스패처 이름은 On 접두사를 사용하고, 필요한 최소한의 파라미터만 포함한다
도전 과제
배운 내용을 직접 실습해보세요
캐릭터 BP에 OnHealthChanged 이벤트 디스패처를 생성하세요(Float 파라미터: NewHealth). 체력이 변경될 때 Call로 브로드캐스트하고, HUD 위젯에서 Bind로 구독하여 체력바를 자동 업데이트하세요.
Door Actor에 OnDoorOpened 디스패처를 만들고, 플레이어가 상호작용하면 Broadcast합니다. 근처의 Guard AI가 이 이벤트를 구독하여 문이 열리면 해당 위치로 이동하는 로직을 구현하세요.
퀘스트 시스템을 이벤트 디스패처로 구현하세요. QuestManager에 OnQuestCompleted, OnQuestFailed 디스패처를 만들고, UI, 사운드, 보상 시스템이 각각 독립적으로 구독하여 반응하는 느슨한 결합 아키텍처를 설계하세요.