• 티스토리 홈
  • 프로필사진
    유니얼
  • 방명록
  • 공지사항
  • 태그
  • 블로그 관리
  • 글 작성
유니얼
  • 프로필사진
    유니얼
    • 분류 전체보기 (295)
      • Unity (17)
        • 게임 개발 (5)
      • Unreal (24)
        • 게임 개발 (20)
      • DirectX (36)
      • 코딩테스트 (91)
        • 프로그래머스 (25)
        • 백준 (66)
      • Google Workspace (1)
      • Programing (102)
        • C# (68)
        • C++ (24)
        • JavaScript (10)
      • 게임 서버 프로그래밍 (17)
      • Web (6)
        • 슈퍼코딩 (6)
  • 방문자 수
    • 전체:
    • 오늘:
    • 어제:
  • 최근 댓글
    등록된 댓글이 없습니다.
  • 최근 공지
    등록된 공지가 없습니다.
# Home
# 공지사항
#
# 태그
# 검색결과
# 방명록
  • Unity Tip : EventBus(이벤트버스)
    2023년 11월 12일
    • 유니얼
    • 작성자
    • 2023.11.12.:06
    728x90

    게임 개발에서 이벤트 버스 패턴은 중요한 개념 중 하나입니다. 이 패턴은 게임 오브젝트 간의 통신과 상호작용을 더 간단하게 만들고, 코드의 모듈화 및 유지 보수를 향상할 수 있습니다. Unity에서 이벤트 버스 패턴을 사용하여 게임 개발을 어떻게 더 효율적으로 할 수 있는지 알아보겠습니다.

     

    참고 자료 링크 :

    https://www.yes24.com/Product/Goods/114854688

     

    유니티로 배우는 게임 디자인 패턴 - 예스24

    우아하게 게임을 개발하고 싶다면 디자인 패턴을 배우자 이 책은 유니티의 고급 프로그래밍 기술과 디자인 패턴으로 작업을 시작할 준비가 된 모든 게임 개발자를 위해 쓰였다. 소프트웨어 디

    www.yes24.com

    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 컴포넌트를 추가합니다.

    EventBus_Result

    실행결과 : 

    마무리

    이벤트 버스 패턴은 Unity 게임 개발에서 강력한 도구 중 하나로, 게임의 모듈화와 유지 보수성을 향상할 수 있습니다. 적절하게 사용하면 코드의 가독성을 향상하고 다양한 컴포넌트 간의 효율적인 통신을 도울 수 있습니다. 이 패턴을 적용하여 더욱 효율적인 Unity 게임을 개발할 수 있습니다.

    반응형
    저작자표시 비영리 변경금지 (새창열림)
    다음글
    다음 글이 없습니다.
    이전글
    이전 글이 없습니다.
    댓글
조회된 결과가 없습니다.
스킨 업데이트 안내
현재 이용하고 계신 스킨의 버전보다 더 높은 최신 버전이 감지 되었습니다. 최신버전 스킨 파일을 다운로드 받을 수 있는 페이지로 이동하시겠습니까?
("아니오" 를 선택할 시 30일 동안 최신 버전이 감지되어도 모달 창이 표시되지 않습니다.)
목차
표시할 목차가 없습니다.
    • 안녕하세요
    • 감사해요
    • 잘있어요

    티스토리툴바