유니티와 디자인 패턴 上

이준호·2024년 1월 13일
0

📌 싱글턴 패턴으로 게임 매니저 구현

  • 다른 분야에서는 논란이 있는 패턴이지만, 유니티에서는 정말 많이 쓰이는 패턴
  • 개별 매니저 클래스에서 핵심 시스템을 래핑하고 관리함
  • 게임 내부를 캡슐화. 직관적인 인터페이스
  • 핵심 구성 요소 간의 결합을 강력하게 하여 유닛 테스트를 어렵게 만듬

➔ 싱글턴 패턴

  • 싱글턴 패턴은 유일성을 보장하는 것

  • 런타임 동안 메모리에 오로지 하나의 인스턴스만 존재한다.

  • 전역적으로 접근할 수 있는 시스템이다.

  • 싱글턴 인스턴스는 메모리 안에 한번 생성, 같은 유형의 인스턴스가 있으면 삭제한다.

장점

  • 전역적 접근 가능

  • 공유 자원에 동시 접근을 제한하거나 사용할 수 있다.

단점

  • 유닛 테스트가 어렵다.

  • 잘못된 프로그래밍 습관을 유발한다.




➔ 게임 매니저 구현

public class Singleton <T>: MonoBehaviour where T: Component
{
    private static T_instance;

    public static T instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = FindObjectOfType<T>();

                if (_instance == null)
                {
                    GameObject obj = new GameObject();
                    obj.name = typeof(T).Name;
                    _instance = obj.AddComponent<T>();
                }
            }

            return instance;
        }
    }

    public void Awake()
    {
        if (_instance == null)
        {
            _instance = this as T;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }
}

public class GameManager : Singleton<GameManager>
{
    void Start()
    {

    }
}
  • 빈 유니티 씬을 만든다.

  • GameObject를 추가하고 GameManager클래스를 추가한다.

  • 원하는 만큼의 유니티 씬을 File ➔ Build Settings 목록에 추가












상태 패턴으로 캐릭터 상태 관리하기

  • 캐릭터는 상태가 끊임없이 전환된다.
  • 상태를 관리하는게 상태 패턴

➔ 상태 패턴 개요

  • 객체가 내부 상태를 기반으로 움직이는 시스템

  • Context 클래스는 객체의 내부 상태를 변경하도록 요청하는 클래스

  • IState 인터페이스는 구체적인 상태 클래스로 연결할 수 있도록 설정한다.

  • Context 오브젝트가 IState 인터페이스를 구현한 클래스들을 호출한다.

장점

  • 캡슐화가 잘 되어있다.

  • 긴 조건문이나 방대한 클래스를 구현하는 것을 막는다.

단점

  • 만약 상태가 전환되는 사이에서 발생하는건 따로 구현을 해야한다.



➔ State_Context.cs

public class CharaccterStateContext
    {
        public ICharacterState CurrentState
        {
            get; set;
        }
        
        private readonly CharacterController _characterController;

        public CharacterStateContext(CharacterController characterController)
        {
            _characterController = characterController;
        }

        public void Transition()
        {
            CurrentState.Handle(_characterController);
        }
        
        public void Transition(ICharacterState state)
        {
            CurrentState = state;
            CurrentState.Handle(_characterController);
        }
    }



➔ State_Controller.cs

public class CharacterController : MonoBehaviour {
            
    private ICharacterState 
        _startState, _stopState, _turnState;
    
    private CharacterStateContext _characterStateContext;

    private void Start() {
        _chracterStateContext = 
            new CharacterStateContext(this);
        
        _startState = 
            gameObject.AddComponent<CharacterStartState>();
        _stopState = 
            gameObject.AddComponent<CharacterStopState>();
        _turnState = 
            gameObject.AddComponent<CharacterTurnState>();
        
        _characterStateContext.Transition(_stopState);
    }

    public void StartCharacter() {
        _characterStateContext.Transition(_startState);
    }

    public void StopCharacter() {
        _chracterStateContext.Transition(_stopState);
    }
}



➔ State_Example.cs

 public class CharacterStartState : MonoBehaviour, ICharacterState
{
    private CharacterController _characterController; 
    
    public void Handle(CharacterController characterController)
    {
        if (!_characterController)
            _characterController = characterController;
        
        _characterController.CurrentSpeed = 10.0f;
    }
}



➔ State_Interface.cs

public interface ICharacterState
{
    void Handle(CharacterController controller);
}











이벤트 버스 패턴으로 게임 이벤트 관리

  • 이벤트 버스는 전역 이벤트를 관리하는 중앙 허브 개념
  • 게임에서 월드 이벤트 발생시 캐릭터들에게 이벤트를 발송하는 식
  • 구현하기 의외로 매우 간단해서 많이 쓰인다.

➔ 이벤트 버스 패턴 개요

  • 어떤 객체에서 이벤트가 발생하면 다른 구독자가 신호를 받는 시스템

  • 발행 / 구독 패턴

  • 발행자랑 구독자는 서로 인식하지 못한다. 중간에 이벤트 버스가 이를 관리하기 때문이다.

장점

  • 오브젝트를 직접 참조하지 않고, 이벤트를 통신할 수 있다.

  • 이벤트 버스는 구독 시스템을 쉽게 구현하게 만든다.

  • 게임 프로토타입 만들때 많이 쓰인다. 쉽고 빠르기 때문.

단점

  • 약간의 성능 비용

  • 이벤트 버스가 static 전역 변수라서, 전역변수의 단점을 모두 가지게 된다.




➔ EventBus.cs

public class WorldEventBus
{
    private static readonly 
        IDictionary<WorldEventType, UnityEvent> 
        Events = new Dictionary<WorldEventType, UnityEvent>();

    public static void Subscribe
        (WorldEventType 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);
        }
    }

    public static void Unsubscribe
        (WorldEventType type, UnityAction listener) {

        UnityEvent thisEvent;

        if (Events.TryGetValue(type, out thisEvent)) {
            thisEvent.RemoveListener(listener);
        }
    }

    public static void Publish(WorldEventType type) {

        UnityEvent thisEvent;

        if (Events.TryGetValue(type, out thisEvent)) {
            thisEvent.Invoke();
        }
    }
}



➔ EventBusType.cs

public enum WorldEventType
{
    COUNTDOWN, START, PAUSE, STOP, FINISH, RESTART, QUIT
}
profile
No Easy Day

0개의 댓글