유니티에서는 적당히 타협한 옵저버 패턴이 사용되곤 하는데요, 패턴까지 들어가지 않더라도 간단히 구조만 잡아본다면...
class TetrisGame {
public static TetrisGame instance;
public Action onGameStart;
public Action onGameEnd;
public Action onGamePause;
public Action onGameResume;
bool isPaused;
void Init() {
instance = this;
}
void GameSequence() {
// 게임 로직
onGameStart.Invoke();
onGameEnd.Invoke();
}
void TogglePause() {
isPaused = !isPaused;
if (isPaused) onGamePause.Invoke();
else onGameResume.Invoke();
}
}
class InGameUI {
void Init() {
TetrisGame.instance.onGameStart += OnGameStart;
TetrisGame.instance.onGameEnd+= OnGameEnd;
TetrisGame.instance.onGamePause+= OnGamePause;
TetrisGame.instance.onGameResume+= OnGameResume;
}
void OnGameStart() { OpenMenu(); }
void OnGameEnd() { CloseMenu(); }
void OnGamePause() { OpenPauseMenu(); }
void OnGameResume() { ClosePauseMenu(); }
void OpenMenu() { }
void CloseMenu() { }
void OpenPauseMenu() { }
void ClosePauseMenu() { }
}
파사드 클래스 없이 직접 연결하여 호출하되 게임 매니저에서는 UI 코드가 존재하지 않게 됩니다.
UI 클래스를 싹 지우더라도 게임 인스턴스 클래스를 잘 돌아가고 있는거죠.
이걸 보고 '게임 코드'는 UI 코드를 모른다고 표현할 수 있겠지만, 코드를 작성하시는 분께서는 알고 염두에 두면서 구조를 짜게 되는 셈입니다.
이렇게 단순히 UI만 분리할거면 왜 필요한건가에 대한 의문이 들 수 있지만 아래와 같은 식으로 여러 모듈을 붙일수도 있게됩니다.
class LocalSpeaker {
void Init() {
TetrisGame.instance.onGameStart += OnGameStart;
TetrisGame.instance.onGameEnd+= OnGameEnd;
TetrisGame.instance.onGamePause+= OnGamePause;
TetrisGame.instance.onGameResume+= OnGameResume;
}
void OnGameStart() { LoadBgmAndPlay(); }
void OnGameEnd() { LoadLobbyMusic(); }
void OnGamePause() { PauseMusic(); }
void OnGameResume() { ResumeMusic(); }
void LoadBgmAndPlay() { }
void LoadLobbyMusic() { }
void PauseMusic() { }
void ResumeMusic() { }
}
극단적인 예시일 수 있지만 게임 인스턴스에서 일어나는 몇몇 이벤트나 함수에 따라 미리 지정된 델리게이트들이 호출되는 방식입니다. 가급적 간단하게 방식을 소개하기 위해 이런 코드를 사용했지만 MVP, Reactive 방식 등 구현체의 종류는 다양합니다. 대표적으로 유니티에서는 단편화된 옵저버패턴을 위해 UniRx가 많이 사용됩니다.
이렇게 'UI는 게임코드를 알고 구독해야하지만, 게임코드는 UI가 뭘 하는지 알 필요 없다' 에 대한 괜찮은 설명이 되었길 빕니다.
참고로, 유니티에서는 어셈블리 정의 파일을 이용해서 강제적으로 이런 구조를 채택할 수 있습니다. 닷넷 어셈블리간에 Circular Refernece가 안 되므로 Runtime과 UI를 다른 어셈블리로 쪼개게 되면 일방향의 레퍼런스 방식을 가지게 됩니다. 자연스럽게 UI가 Runtime 코드를 참조해서 UI 로직을 작성하게됩니다.