[ 게임 개발 ] FSM 구조와 이벤트 호출에 대한 고찰
최근 여러 게임잼들을 겪으며 Player구동 로직으로 FSM을 아주 애용을 했었다.
또한 그런 로직을 통해 외부의 이벤트를 호출할 일이 많았다.
이건 왜 그러냐 하면 Feedback이라는 Create 핸들함수를 실행시키면 정해진 동작,
사운드 재생, VFX 재생, 카메라 효과, 볼륨 효과들을 컨트롤하는 기능들을 수행해 주는데
이 Create함수를 발행할 때 UnityEvent가 쓰였기 때문이다.
그런데 기존에 사용 중인 FSM은 MonoBehaviour기반이 아니기 때문에 이벤트고 뭐고
직렬화가 되어있지 않아 직렬화된 다른 대상을 매개로 이벤트를 Invoke 해주어야만 했다.
그 매개의 대상이 마침 모든 State들이 가지고 있는 Player였다.
그러다 보니 Player에 모든 Event들이 다 박혀있고, Player FSM내부에서 어떤 기능에 대한 부가적인
효과이벤트들을 발행해 주기 위해 플레이어를 참조해 Invoke해주는 기괴한 상황이 발생했다.
플레이어를 참조하는 것은 State 내에서 필수적으로 일어나는 일이기 때문에 어쩔 수 없는데.
이벤트가 플레이어에 다 박혀있는 모습이 너무 보기 안좋았다.
게임잼이라 코드 구조에 대한 강박을 어느 정도 풀고 개발했다지만 자꾸 이후 다른 프로젝트 개발을
진행할 때에도 조금씩 막히던 문제였다. 오늘은 이 문제를 완전히 해결해보고자 한다.
첫 번째로 떠올린 방법이
우선 피드백들을 묶어주고 코드 내에서 호출해 줄 다른 객체를 만드는 것이었다.
이 방법을 사용하기 위해 이와 같이 피드백들의 상위에
EventController를 달아준다.
또한 EventController 내부에서는
자기 자식들로 달린 FeedbackPlayer들을 모두 GetComponentsInChildren을 통해 가져오고
이를 Dictionary를 통해 관리해 준다.
또한 Dictionary에서 찾아서 Invoke와 Finish를 걸어줄 함수를 만들어주고
FSM내부에서 StateMachine을 통해 이를 호출할 것을 상상을 했다.
하지만 이렇게 되면 StateMachine의 기능적인 정체성이
모호해진다. State들만 관리해주어야 할 StateMachine이 FeedbackEventController 가질만한 이유가 없다는 것이다.
이는 객체지향 SOLID법칙의 단일책임 S원칙에 위배되는
코드인 것이다.
이를 어떻게 해결하는가...
해결
이에 관해서 대 선생님께 질문을 구해보았다
이 SOLID 위반 문제를 해결해 줄 만능 해결사는
EventChannel이라는 시스템인데, 그냥 싱글톤처럼 쓰고 싶은 곳에서 냅다 가져다 쓸 수 있는 것인데.
SO라서 암튼 싱글톤은 아님
사용법은 대충 직렬화시킨 곳에 EventChannelSO를 넣어놓고, 일반적인 Action처럼
구독하고 Invoke해주는 방식으로 사용된다. 다만 인자를 넘기는 방식이 조금 다른데
이런 식으로 데이터 넘기기용 클래스를 만들어서 넘겨주어야 한다
Action을 쓸 때마다 SO를 만들어주지 않기 위해 한 채널에서 타입별로 이를 담아뒀다가
Invoke 할 때 넘어온 타입에 맞는 Action을 Invoke 해준다.
이 시스템을 이용해 간접적으로 종속성을 가지면서 Feedback을 관리함과
동시에 StateMachine의 단일책임 규칙을 지켜줄 수 있다.
모든 State들은 기본적으로 Player에 대한 종속성을 가지고 있기 때문에
Player에 대한 기존의 종속성만을 유지한 채로 구현되었다.
이제 스테이트 내에서 이런 식으로 활용만 해주면 되는 것이다.
오늘도 다들 멋진 코드 짜시기 바란다.