본문 바로가기
Studies/게임 디자인 패턴

[게임 디자인 패턴] 이벤트 버스

by NCTP 2024. 7. 16.

// 유니티로 배우는 게임 디자인 패턴 제 2판 (데이비드 바론, 구진수)에 대한 공부 요약 글입니다.

 

개요

  • 이벤트 버스는 객체가 구독하거나 게시할 수 있는 특정한 전역 이벤트의 목록을 관리하는 중앙 허브 역할을 한다.
  • 이벤트 관리와 관련된 가장 간단한 패턴이며, 한 줄의 코드로 객체에 구독자 혹은 게시자의 역할을 할당하는 과정을 줄인다.
  • 빠른 결과가 필요할 때 유용한 디자인 패턴이다. (빠른 프로토타이핑이 필요할 때 유용하다!)
  • 복잡한 이벤트 타입 / 구조체를 다루지 않을 때 아주 유용하다.

게시자(Publisher)가 이벤트를 발생하면, 게시자로부터 구독자(Subscriber)가 받을 수 있는 신호를 보내게 된다. 

해당 신호는 구독자들만 받게 되며, 어떻게 처리할지 결정한다.

 

해당 과정의 중심에는 이벤트 버스가 존재한다.

게시자와 구독자는 이벤트 버스를 통해 이벤트를 확인하고, 수신하고, 동작한다고 보면 되겠다.

 

매우 정교한, 혹은 복잡한 게임 이벤트를 구성하고 있지 않다면 잘 동작하는 디자인 패턴이다.

물론 활용하기에 따라서는, 대규모 프로젝트나 복잡한 상호작용이 필요한 게임에서도 유용하게 사용될 수 있다.

 

핵심 키워드

  • 게시자 (Publisher) : 어떠한 이벤트를 게시하는 게시자. 구독자들이 확인이 가능하다.
  • 구독자 (Subscriber) : 이벤트 버스를 통해 특정 이벤트를 구독한다. 게시자가 게시한 이벤트를 확인하고, 동작을 결정한다.
  • 이벤트 버스 : 게시자와 구독자 사이의 연결 플랫폼 역할. 구독자와 게시자 사이의 이벤트 전송이 버스의 주된 역할이다.

 

장점과 단점

장점

  • 분리 및 느슨한 결합 : 이벤트 시스템을 사용할 때 주요 이점은 오브젝트를 분리한다는 점이다. 오브젝트끼리 서로를 참조하지 않고 이벤트를 통해 통신한다.
  • 단순성 : 패턴 자체가 이해하기 쉽고 단순하다. 구독자와 게시자가 있고, 구독자는 게시자의 게시물을 확인할 수 있다는 메커니즘을 추상화한 형태이기 때문이다.
  • 재사용성 : 이벤트 핸들러는 다양한 객체에서 재사용할 수 있다.

단점

  • 성능문제와 오버헤드 : 오브젝트 간 메시지를 관리하는 저수준의 메커니즘이 존재한다. 이 때문에 이벤트 시스템을 사용할 때 크고 작은 성능 비용이 발생할 수 있다.
  • 디버깅의 어려움 : 전역적 접근으로 인해 이벤트 흐름이 명시적이지 못해서 디버깅에 어려움이 생길 수 있다.

 

구현하기 - 러닝 액션 게임을 예시로

러닝 액션 게임에 들어갈 이벤트에 대한 스켈레톤 코드를 이벤트 버스 패턴으로 구현해보자.

구현할 러닝 액션 게임에는 슈팅게임의 "폭탄"과 같이 적들의 모든 투사체를 삭제하고 데미지를 입히는 특수 스킬이 존재한다.

 

  • To-Do : 플레이어의 특수 스킬 발동 이벤트

를 이벤트 버스 패턴을 통해 구현해보자.

 

먼저, 이벤트 버스를 구현하자.

 

using System.Collections;
using System.Collections.Generic;
using System.Security.Cryptography;
using UnityEngine;
using UnityEngine.Events;

public enum RunningEventType
{
    SKILL
}
public class RunningEventBus : MonoBehaviour
{
    private static readonly IDictionary<RunningEventType, UnityEvent> // 다른 오브젝트가 덮어쓰지 못하도록 private / readonly로 선언
    Events = new Dictionary<RunningEventType, UnityEvent>(); // 이벤트들

    /*
    Subscribe 메서드
    입력 파라미터 : 러닝 이벤트 종류, 콜백 함수
    Subscribe 메서드를 통해 특정 이벤트 타입의 구독자로 자신을 추가.
    */
    public static void Subscribe(RunningEventType eventType, UnityAction listener)
    {
        UnityEvent thisEvent;
        if(Events.TryGetValue(eventType, out thisEvent)) 
        {
            thisEvent.AddListener(listener);
        }
        else
        {
            thisEvent = new UnityEvent();
            thisEvent.AddListener(listener);
            Events.Add(eventType, thisEvent);
        }
    }
    /*
    Unsubscribe 메서드
    입력 파라미터 : 러닝 이벤트 종류, 콜백 함수
    특정 이벤트를 구독하는 오브젝트를 삭제.
    */
    public static void Unsubscribe(RunningEventType eventType, UnityAction listener)
    {
        UnityEvent thisEvent;
        if(Events.TryGetValue(eventType, out thisEvent))
        {
            thisEvent.RemoveListener(listener);
        }
    }

    /*
    Publish 메서드
    입력 파라미터 : 러닝 이벤트 종류
    특정 이벤트를 게시한다. 특정 이벤트의 구독자들은 확인할 수 있다.
    */
    public static void Publish(RunningEventType eventType)
    {
        UnityEvent thisEvent;
        if(Events.TryGetValue(eventType, out thisEvent))
        {
            thisEvent.Invoke();
        }
    }

}

 

이제 이벤트 버스를 통해 특정 이벤트를 구독, 구독취소할 수 있고, 

이벤트 버스를 통해 이벤트를 게시할 수 있다.

 

이벤트 버스를 통해 이벤트를 게시하면, 이벤트를 구독한 객체들이 구독할 때 지정했던 콜백 함수를 실행할 것이다!

 

    void OnEnable()
    {
        Debug.Log("I'm Enabled!");
        RunningEventBus.Subscribe(RunningEventType.SKILL, SpecialSkill);
    }
    void OnDisable()
    {
        Debug.Log("I'm Disabled!");
        RunningEventBus.Unsubscribe(RunningEventType.SKILL, SpecialSkill);
    }
    void SpecialSkill()
    {
        Debug.Log("Use the Special Skill!");
    }

 

Player.cs 파일에서 다음과 같은 코드를 작성했다.

SKILL 이벤트 타입을 구독했고, SKILL 이벤트 발생 시 SpecialSkill 메서드가 호출된다.

 

또한, OnEnable(), OnDisable()에서 구독 / 구독 취소를 해줌으로써

객체가 활성화되었을 때만 이벤트를 수신하도록 한다.

 

        if(Input.GetKeyDown(KeyCode.Q))
        {
            RunningEventBus.Publish(RunningEventType.SKILL);
        }

 

이제 Q 키를 누르면, SKILL 이벤트가 발생한다.

 

결과를 확인해보면 ..!

 

SKILL 이벤트 발생에 따라 SpecialSkill 메서드가 정상적으로 호출된 모습.

 

잘 동작함을 알 수 있다.

 

마치며

  이번 글에서는 유니티에서 이벤트를 게시하고 구독하는 과정을 단순화하는 디자인 패턴인 이벤트 버스 패턴을 간단하게 구현해보고, 실제 동작까지 확인해보았다. 

 

  객체들의 느슨한 결합을 돕지만, 컴퓨터 자원을 다소 잡아먹고 디버깅에 어려움이 생길 수 있는 디자인 패턴이기 때문에, 활용하기 전 다른 디자인 패턴들도 고려해보면서 사용하자.

 

  

 

댓글