-
Unity Tip : EventBus(이벤트버스)2023년 11월 12일
- 유니얼
-
작성자
-
2023.11.12.:06
728x90게임 개발에서 이벤트 버스 패턴은 중요한 개념 중 하나입니다. 이 패턴은 게임 오브젝트 간의 통신과 상호작용을 더 간단하게 만들고, 코드의 모듈화 및 유지 보수를 향상할 수 있습니다. Unity에서 이벤트 버스 패턴을 사용하여 게임 개발을 어떻게 더 효율적으로 할 수 있는지 알아보겠습니다.
참고 자료 링크 :
EventBus
이벤트버스란?
이벤트 버스는 소프트웨어 아키텍처 패턴 중 하나로, 다른 컴포넌트나 모듈 간의 통신을 관리하기 위해 사용됩니다. 이 패턴은 이벤트를 발행(emit)하고, 해당 이벤트를 구독(subscribe)하는 리스너에게 전달함으로써 컴포넌트 간의 결합도를 낮추고, 코드의 모듈화를 촉진합니다.
이벤트 버스 패턴의 장점
- 느슨한 결합: 컴포넌트 간의 강한 의존성을 피하고 느슨한 결합을 유지할 수 있습니다.
- 모듈화: 코드를 모듈화 하여 개발 및 유지 보수를 더 쉽게 할 수 있습니다.
- 확장성: 새로운 이벤트 및 리스너를 추가하기 쉽습니다.
주의사항과 고려 사항
- 과용하지 않기: 모든 것을 이벤트로 처리하는 것은 오히려 복잡성을 높일 수 있습니다.
- 성능 고려: 이벤트 처리가 빈번한 경우 성능 문제를 고려해야 합니다.
Unity에서 이벤트 버스 구현하기
Step 1 : 이벤트 클래스 구현하기
먼저, 게임에서 발생할 수 있는 각각의 이벤트를 나타내는 이벤트 클래스를 정의합니다. 이 클래스는 이벤트의 데이터 및 관련 정보를 포함합니다. 이 예제에서는 GameEventType 열거형과 GameStatesEvent 구조체를 사용한 것처럼 각 이벤트 유형과 관련된 정보를 포함하는 클래스를 정의합니다.
// 게임 이벤트 종류를 정의하는 열거형 public enum GameEventType { COUNTDOWN, // 카운트 다운 이벤트 START, // 시작 이벤트 RESTART, // 재시작 이벤트 PAUSE, // 일시 정지 이벤트 STOP, // 중지 이벤트 FINISH, // 완료 이벤트 QUIT, // 종료 이벤트 } // 게임 상태 이벤트를 나타내는 구조체 public struct GameStatesEvent { public GameEventType gameEventType; // 게임 이벤트 타입을 저장하는 변수 // GameStatesEvent 구조체의 생성자 public GameStatesEvent(GameEventType gameEventType) { this.gameEventType = gameEventType; // 생성자를 통해 게임 이벤트 타입을 초기화합니다. } // GameStatesEvent 구조체의 인스턴스를 트리거(발생)하는 정적 메서드 static GameStatesEvent e; public static void Trigger(GameEventType gameEventType) { e.gameEventType = gameEventType; // gameEventType 변수에 전달된 이벤트 타입을 저장합니다. GameEventManager.TriggerEvent<GameStatesEvent>(e); // GameEventManager 클래스를 사용하여 이벤트를 트리거합니다. } }
Step 2 : 이벤트 리스너 인터페이스 구현하기
이제 이벤트를 수신하고 처리할 수 있는 리스너 인터페이스를 정의합니다. 예를 들어, 다음과 같이 EventListener 인터페이스를 만들 수 있습니다.
// 이벤트 리스너 베이스 인터페이스 public interface EventListenerBase { } // 이벤트 리스너 인터페이스 public interface EventListener<T> : EventListenerBase { void OnEvent(T eventType); }
Step 3 : 이벤트 버스 관리 클래스(GameEventManger) 구현하기
이벤트를 관리하고 통신을 중계할 이벤트 버스 관리 클래스를 만듭니다. 이 클래스는 이벤트를 발행하고 구독하는 메서드를 포함합니다. 또한, 이벤트를 수신한 리스너에게 전달하는 로직을 구현합니다.
// 게임 이벤트 관리자 클래스 public class GameEventManager { // 이벤트 타입별로 구독자(리스너) 목록을 관리하는 사전 private static readonly Dictionary<Type, List<EventListenerBase>> _subscribersList = new Dictionary<Type, List<EventListenerBase>>(); // 이벤트를 구독하는 메서드 public static void Subscribe<GameEvents>(EventListener<GameEvents> listener) where GameEvents : struct { Type eventType = typeof(GameEvents); if (!_subscribersList.ContainsKey(eventType)) { _subscribersList[eventType] = new List<EventListenerBase>(); } if (!SubscriptionExists(eventType, listener)) { _subscribersList[eventType].Add(listener); } } // 이벤트 구독 취소 메서드 public static void Unsubscribe<GameEvents>(EventListener<GameEvents> listener) where GameEvents : struct { Type eventType = typeof(GameEvents); if (!_subscribersList.ContainsKey(eventType)) return; List<EventListenerBase> subscriberList = _subscribersList[eventType]; for (int i = subscriberList.Count - 1; i >= 0; i--) { if (subscriberList[i] == listener) { subscriberList.Remove(subscriberList[i]); if (subscriberList.Count == 0) { _subscribersList.Remove(eventType); } return; } } } // 이미 구독 중인지 확인하는 메서드 private static bool SubscriptionExists(Type type, EventListenerBase receiver) { List<EventListenerBase> receivers; if (!_subscribersList.TryGetValue(type, out receivers)) return false; bool exists = false; for (int i = receivers.Count - 1; i >= 0; i--) { if (receivers[i] == receiver) { exists = true; break; } } return exists; } // 이벤트를 발생시키는 메서드 public static void TriggerEvent<GameEvents>(GameEvents events) where GameEvents : struct { Type eventType = typeof(GameEvents); List<EventListenerBase> list; if (!_subscribersList.TryGetValue(eventType, out list)) { return; } for (int i = list.Count - 1; i >= 0; i--) { (list[i] as EventListener<GameEvents>).OnEvent(events); } } } // 이벤트 리스너를 등록 및 해제하는 확장 메서드 public static class GameEventsRegister { public delegate void Delegate<T>(T eventType); // 이벤트 리스너를 등록하는 확장 메서드 public static void EventStartingListening<EventType>(this EventListener<EventType> caller) where EventType : struct { GameEventManager.Subscribe<EventType>(caller); } // 이벤트 리스너를 해제하는 확장 메서드 public static void EventStopListening<EventType>(this EventListener<EventType> caller) where EventType : struct { GameEventManager.Unsubscribe<EventType>(caller); } }
Step 4 : 이벤트 발행 및 구독
이제 게임 오브젝트에서 필요한 이벤트를 구독하고, 이벤트를 발생시킬 때 해당 이벤트를 트리거합니다.
PlayerController.cs
이 코드는 Unity 게임에서 상태 변화를 처리하기 위한 스크립트입니다. 아래는 코드의 주요 기능과 설명입니다:
using UnityEngine; public class PlayerController : MonoBehaviour, EventListener<GameStatesEvent> { private string _status; private void OnEnable() { // GameStatesEvent 이벤트를 구독합니다. this.EventStartingListening<GameStatesEvent>(); } private void OnDisable() { // 스크립트 비활성화 시 이벤트 구독을 해제합니다. this.EventStopListening<GameStatesEvent>(); } private void GameStart() { // 게임 시작 시 상태를 업데이트합니다. _status = "게임 시작됨"; } private void GameStopped() { // 게임 정지 시 상태를 업데이트합니다. _status = "게임 정지됨"; } private void OnGUI() { // GUI에 게임 상태를 표시합니다. GUI.color = Color.green; GUI.Label(new Rect(10, 60, 200, 20), "게임 상태: " + _status); } public void OnEvent(GameStatesEvent eventType) { // GameStatesEvent 이벤트가 발생하면 처리하는 메서드입니다. switch (eventType.gameEventType) { case GameEventType.START: GameStart(); break; case GameEventType.STOP: GameStopped(); break; } } }
- OnEnable 메서드: 스크립트가 활성화될 때 호출되며, GameStatesEvent 이벤트를 구독합니다. 이렇게 하면 이벤트가 발생할 때 해당 이벤트 핸들러가 호출됩니다.
- OnDisable 메서드: 스크립트가 비활성화될 때 호출되며, 이벤트 구독을 해제합니다. 스크립트가 비활성화되면 더 이상 이벤트를 수신하지 않습니다.
- GameStart 메서드: 게임 시작 상태를 처리하는 메서드로, 상태를 업데이트하여 _status 변수에 "게임 시작됨"을 할당합니다.
- GameStopped 메서드: 게임 정지 상태를 처리하는 메서드로, 상태를 업데이트하여 _status 변수에 "게임 정지됨"을 할당합니다.
- OnGUI 메서드: GUI에 게임 상태를 표시하는 메서드로, _status 변수에 저장된 상태를 녹색으로 표시합니다.
- OnEvent 메서드: GameStatesEvent 이벤트를 처리하는 메서드입니다. 이벤트 타입에 따라 GameStart 또는 GameStopped 메서드를 호출하여 게임 상태를 업데이트합니다.
HUDController.cs
이 코드는 Unity 게임에서 HUD (Head-Up Display)를 관리하는 스크립트를 나타냅니다. 코드의 주요 기능과 설명은 다음과 같습니다:
using UnityEngine; public class HUDController : MonoBehaviour, EventListener<GameStatesEvent> { private bool _isDisplayOn; // HUD 표시 여부를 나타내는 변수 private void OnEnable() { // 이 스크립트가 활성화될 때 GameStatesEvent 이벤트를 구독합니다. this.EventStartingListening<GameStatesEvent>(); } private void OnDisable() { // 이 스크립트가 비활성화될 때 이벤트 구독을 해제합니다. this.EventStopListening<GameStatesEvent>(); } private void DisplayHUD() { // HUD를 표시하는 메서드로, _isDisplayOn을 true로 설정합니다. _isDisplayOn = true; } private void OnGUI() { if (_isDisplayOn) { // HUD가 표시 중일 때 if (GUILayout.Button("게임 정지")) { // "게임 정지" 버튼을 누르면 HUD를 숨기고 게임 정지 이벤트를 트리거합니다. _isDisplayOn = false; GameEventManager.TriggerEvent(new GameStatesEvent { gameEventType = GameEventType.STOP }); } } } public void OnEvent(GameStatesEvent eventType) { // GameStatesEvent 이벤트를 처리하는 메서드입니다. switch (eventType.gameEventType) { case GameEventType.START: // 게임 시작 이벤트를 받으면 HUD를 표시합니다. DisplayHUD(); break; } } }
- OnEnable 메서드: 스크립트가 활성화될 때 호출되며, GameStatesEvent 이벤트를 구독합니다. 이렇게 하면 이벤트가 발생할 때 해당 이벤트 핸들러가 호출됩니다.
- OnDisable 메서드: 스크립트가 비활성화될 때 호출되며, 이벤트 구독을 해제합니다. 스크립트가 비활성화되면 더 이상 이벤트를 수신하지 않습니다.
- DisplayHUD 메서드: HUD를 표시하는 메서드로, _isDisplayOn 변수를 true로 설정하여 HUD가 표시됨을 나타냅니다.
- OnGUI 메서드: GUI를 그리는 메서드로, HUD가 표시 중일 때 "게임 정지" 버튼을 표시하고 클릭하면 HUD를 숨기고 게임 정지 이벤트를 트리거합니다.
- OnEvent 메서드: GameStatesEvent 이벤트를 처리하는 메서드입니다. 이벤트 타입에 따라 게임이 시작되면 HUD를 표시합니다.
이 코드를 사용하면 게임 시작 시 HUD를 표시하고 "게임 정지" 버튼을 클릭하여 HUD를 숨길 수 있으며, 게임 정지 이벤트를 트리거하여 게임 상태를 제어할 수 있습니다.
CountDownTimer.cs
이 코드는 Unity 게임에서 카운트다운을 관리하는 스크립트를 나타냅니다. 코드의 주요 기능과 설명은 다음과 같습니다:
using UnityEngine; public class CountDownTimer : MonoBehaviour, EventListener<GameStatesEvent> { private float _currentTime; // 현재 카운트다운 시간을 나타내는 변수 private float _duration = 3.0f; // 카운트다운 기간을 설정하는 변수 private void OnEnable() { // 이 스크립트가 활성화될 때 GameStatesEvent 이벤트를 구독합니다. this.EventStartingListening<GameStatesEvent>(); } private void OnDisable() { // 이 스크립트가 비활성화될 때 이벤트 구독을 해제합니다. this.EventStopListening<GameStatesEvent>(); } private void StartTimer() { // 카운트다운을 시작하는 메서드입니다. StartCoroutine(CountDown()); } private IEnumerator CountDown() { _currentTime = _duration; while (_currentTime > 0) { yield return new WaitForSeconds(1.0f); _currentTime--; } // 카운트다운이 끝나면 게임 시작 이벤트를 트리거합니다. GameEventManager.TriggerEvent(new GameStatesEvent { gameEventType = GameEventType.START }); } public void OnEvent(GameStatesEvent eventType) { // GameStatesEvent 이벤트를 처리하는 메서드입니다. switch (eventType.gameEventType) { case GameEventType.COUNTDOWN: // COUNTDOWN 이벤트를 받으면 카운트다운을 시작합니다. StartTimer(); break; } } private void OnGUI() { // GUI에 현재 카운트다운 시간을 표시합니다. GUI.color = Color.blue; GUI.Label(new Rect(125, 0, 100, 20), "남은 시간: " + _currentTime); } }
- OnEnable 메서드: 스크립트가 활성화될 때 호출되며, GameStatesEvent 이벤트를 구독합니다. 이렇게 하면 이벤트가 발생할 때 해당 이벤트 핸들러가 호출됩니다.
- OnDisable 메서드: 스크립트가 비활성화될 때 호출되며, 이벤트 구독을 해제합니다. 스크립트가 비활성화되면 더 이상 이벤트를 수신하지 않습니다.
- StartTimer 메서드: 카운트다운을 시작하는 메서드로, CountDown 코루틴을 시작합니다.
- CountDown 코루틴: 지정된 기간 동안 카운트다운을 진행하고, 시간이 끝나면 게임 시작 이벤트를 트리거합니다.
- OnEvent 메서드: GameStatesEvent 이벤트를 처리하는 메서드입니다. 이벤트 타입에 따라 COUNTDOWN 이벤트를 받으면 카운트다운을 시작합니다.
- OnGUI 메서드: GUI를 그리는 메서드로, 현재 카운트다운 시간을 표시합니다.
이 코드를 사용하면 COUNTDOWN 이벤트를 트리거하여 카운트다운을 시작하고, 화면에 남은 시간을 표시할 수 있습니다. 카운트다운이 끝나면 게임 시작 이벤트를 트리거하여 게임을 시작할 수 있습니다.
ClientEventBus.cs
이 코드는 Unity 게임에서 클라이언트 이벤트 버스를 관리하는 스크립트를 나타냅니다. 코드의 주요 기능과 설명은 다음과 같습니다:
using UnityEngine; public class ClientEventBus : MonoBehaviour, EventListener<GameStatesEvent> { private bool _isButtonEnabled; // 버튼 활성화 여부를 나타내는 변수 private void Start() { // HUDController, CountDownTimer, PlayerController 스크립트를 게임 오브젝트에 추가합니다. gameObject.AddComponent<HUDController>(); gameObject.AddComponent<CountDownTimer>(); gameObject.AddComponent<PlayerController>(); _isButtonEnabled = true; // 버튼을 활성화 상태로 초기화합니다. } private void OnEnable() { // 이 스크립트가 활성화될 때 GameStatesEvent 이벤트를 구독합니다. this.EventStartingListening<GameStatesEvent>(); } private void OnDisable() { // 이 스크립트가 비활성화될 때 이벤트 구독을 해제합니다. this.EventStopListening<GameStatesEvent>(); } private void ReStart() { // 재시작 메서드로, 버튼을 다시 활성화 상태로 설정합니다. _isButtonEnabled = true; } private void OnGUI() { if (_isButtonEnabled) { // 버튼이 활성화된 상태일 때 if (GUILayout.Button("카운트다운 시작")) { _isButtonEnabled = false; // 버튼을 비활성화 상태로 변경합니다. GameEventManager.TriggerEvent(new GameStatesEvent { gameEventType = GameEventType.COUNTDOWN }); // COUNTDOWN 이벤트를 트리거합니다. } } } public void OnEvent(GameStatesEvent eventType) { // GameStatesEvent 이벤트를 처리하는 메서드입니다. switch (eventType.gameEventType) { case GameEventType.STOP: // STOP 이벤트를 받으면 ReStart 메서드를 호출하여 버튼을 다시 활성화 상태로 변경합니다. ReStart(); break; } } }
- Start 메서드: 게임 시작 시 실행되며, HUDController, CountDownTimer 및 PlayerController 스크립트를 게임 오브젝트에 추가합니다. 또한 _isButtonEnabled 변수를 true로 초기화하여 버튼을 활성화 상태로 설정합니다.
- OnEnable 메서드: 스크립트가 활성화될 때 호출되며, GameStatesEvent 이벤트를 구독합니다.
- OnDisable 메서드: 스크립트가 비활성화될 때 호출되며, 이벤트 구독을 해제합니다.
- ReStart 메서드: 게임을 재시작하는 메서드로, 버튼을 다시 활성화 상태로 설정합니다.
- OnGUI 메서드: GUI를 그리는 메서드로, _isButtonEnabled 변수가 true인 경우 "카운트다운 시작" 버튼을 표시하고 클릭하면 COUNTDOWN 이벤트를 트리거하여 게임 카운트다운을 시작합니다.
- OnEvent 메서드: GameStatesEvent 이벤트를 처리하는 메서드입니다. STOP 이벤트를 받으면 ReStart 메서드를 호출하여 버튼을 다시 활성화 상태로 변경합니다.
이 코드를 사용하면 게임 시작 시 버튼을 클릭하여 게임 카운트다운을 시작하고, 게임이 정지되면 버튼을 다시 활성화할 수 있습니다. STOP 이벤트를 통해 버튼 상태를 관리하여 게임을 제어할 수 있습니다.
Step 5 : 씬 구성 및 결과 확인하기
이제 빈 씬을 만들고 새로운 GameObject를 생성하고 ClientEventBus 컴포넌트를 추가합니다.
실행결과 :
마무리
이벤트 버스 패턴은 Unity 게임 개발에서 강력한 도구 중 하나로, 게임의 모듈화와 유지 보수성을 향상할 수 있습니다. 적절하게 사용하면 코드의 가독성을 향상하고 다양한 컴포넌트 간의 효율적인 통신을 도울 수 있습니다. 이 패턴을 적용하여 더욱 효율적인 Unity 게임을 개발할 수 있습니다.
반응형다음글이전글이전 글이 없습니다.댓글