Unity의 GameObject가 익숙해질수록 그것에 모든 것을 끼워 맞추려는 충동이 생겼습니다. 최근에 그 충동에서 벗어나는 작업을 시작했습니다.
Unity를 5년 가까이 써왔습니다. 그 안에서 자연스럽게 익숙해진 패턴이 하나 있습니다. 어떤 동작이든 GameObject로 표현하고, MonoBehaviour 컴포넌트를 붙이고, Update 안에서 매 프레임 상태를 갱신하는 방식. 처음엔 이 방식이 정말 강력하다고 생각했습니다. 모든 게 시각적이고, 모든 게 인스펙터에서 조작 가능했으니까요.
그런데 최근 ProjectArc의 적 AI를 작업하면서, 이 방식의 한계가 분명해졌습니다. 상태가 다섯 개를 넘어가는 순간, MonoBehaviour 한 곳에 if-else 체인이 늘어났습니다. 새 상태를 추가할 때마다 기존 동작을 깨뜨리지 않았는지 확인해야 했고, 각 상태의 진입/이탈을 누락하기 시작했습니다.
상태가 흩어지면 일어나는 일
가장 큰 문제는 OnEnable과 OnDisable을 신뢰할 수 없게 된다는 점이었습니다. 하나의 GameObject가 여러 상태를 가질 때, 어떤 상태에서 어떤 컴포넌트를 켜고 꺼야 하는지가 점점 흐려졌습니다. 결국 각 컴포넌트가 자기가 동작해야 하는 조건을 스스로 판단하기 시작했고, 이게 디버깅을 정말 어렵게 만들었습니다.
상태가 한 곳에 모여 있지 않으면, 어디서 무엇이 잘못됐는지 추적이 거의 불가능해진다.
그래서 결국 UE5로 작업을 옮기면서 StateTree를 본격적으로 써보기 시작했습니다. StateTree는 상태 머신과 행동 트리의 결합인데, 각 상태가 명시적으로 진입 조건과 이탈 조건을 가지고 있어서 컴포넌트가 스스로 자기 상태를 판단할 필요가 없습니다.
지금은 어떻게 일하고 있는지
완전히 GameObject 모델을 떠난 건 아닙니다. Unity 작업은 여전히 매일 하고 있고, 그 안에서는 여전히 컴포넌트 기반으로 사고합니다. 다만 새 시스템을 설계할 때 한 가지 질문을 먼저 던지게 됐습니다. "이 객체의 상태가 흩어져 있어도 디버깅 가능한가?"
답이 "아니오"라면, 상태 머신을 하나 두고 시작합니다. 더 이상 모든 걸 GameObject로 분해하지 않습니다. 어쩌면 이게 GameObject를 떠나는 첫걸음일지도 모르겠습니다.