메인 콘텐츠로 건너뛰기

5분 만에 시작하기: 첫 번째 이벤트 드리븐 시스템 구축

TinyGiants
GES Creator & Unity Games & Tools Developer

"시간 5분밖에 없어요. 이벤트 시스템 동작하는 것만 보여주세요."

그래, 충분하다. 이론 없고, 아키텍처 딥다이브 없고, 다른 접근법과의 비교도 없다. Unity 프로젝트에서 제로부터 동작하는 이벤트 기반 인터랙션까지, 빠르게 하고 싶다. 가보자.

이 가이드는 Unity 프로젝트가 열려있고(2021.3 LTS 이상) 약 5분이 있다고 가정한다. 끝나면, 게임에서 뭔가 일어났을 때 발생하는 이벤트가 완전히 별개의 GameObject에서 응답을 트리거하는 것을 갖게 된다 — 둘 사이에 직접 참조 제로.

이벤트 200개 돌파: 이벤트 관리가 무너지는 이유

TinyGiants
GES Creator & Unity Games & Tools Developer

새 Unity 프로젝트를 시작한다. 이벤트 10개를 만든다. OnPlayerDeath, OnScoreChanged, OnLevelComplete. 적절한 이름을 붙이고, 폴더에 넣고, 넘어간다. 인생이 좋다. 전체 이벤트 구조를 머릿속에 담을 수 있다.

6개월이 지났다. 이벤트가 200개다. Project 창은 ScriptableObject 파일의 벽이다. OnPlayerHealthDepleted가 필요한데 — 아니면 OnPlayerHPLow였나? 아니면 OnPlayerHealthZero? 전부 OnPlayer로 시작하는 이름들을 눈을 가늘게 뜨고 스크롤한다. 3분 후 포기하고 새로 하나 만든다, 원하는 이벤트가 이미 있는지조차 확신이 안 서니까.

모든 이벤트 기반 Unity 프로젝트가 결국 도달하는 곳이다. 이벤트 패턴이 잘못돼서가 아니라, 아무도 규모에서 이벤트를 관리하기 위한 도구를 만들지 않았기 때문이다. Unity는 Animation 창, Shader Graph, Timeline, Input System 디버거를 제공한다. 이벤트에는... Project 창뿐이다.

제로 리플렉션, 제로 GC: "고성능" 이벤트 시스템의 진짜 의미

TinyGiants
GES Creator & Unity Games & Tools Developer

Unity Asset Store의 모든 이벤트 시스템 플러그인이 설명 어딘가에 "고성능"을 써놓는다. "쉬운 사용"과 "완벽한 문서" 사이 어딘가에. 그런데 1ms와 0.001ms는 둘 다 사람 기준으로는 빠르지만, 하나가 다른 것보다 천 배 느리다. 플러그인이 "고성능"이라고 할 때, 실제로 무슨 뜻일까? 뭐에 비해서? 어떻게 측정했는데?

예전엔 이걸 신경 안 썼다. 대부분이 안 쓴다. 이벤트 연결하고, 게임 잘 돌아가고, 출시한다. 그런데 수백 개의 엔티티가 각각 여러 이벤트를 수신하는 모바일 프로젝트를 하게 되면서, 갑자기 "고성능"이 마케팅 체크박스가 아니라 — 60 FPS와 슬라이드쇼의 차이가 됐다.

이 글은 이벤트 시스템에서 "고성능"이 실제로 무엇을 의미해야 하는지, 왜 대부분의 구현이 부족한지, 그리고 GES가 Expression Tree 컴파일을 통해 어떻게 거의 제로 오버헤드를 달성하는지에 대한 이야기다. 실제 수치로, 허풍 없이.

Unity 제네릭 직렬화의 벽: 타입 안전한 이벤트에 보일러플레이트 세금은 불필요하다

TinyGiants
GES Creator & Unity Games & Tools Developer

GameEvent<T>를 만들었다. 깔끔하고, 타입 안전하고, 우아하다. 체력 업데이트용 GameEvent<float> 필드를 만들고 [SerializeField]를 붙였다. Inspector로 전환했다. 필드가 안 보인다. 그냥... 없다. Unity가 0으로 나누기를 요청한 것처럼 빈 패널로 쳐다보고 있다.

Unity의 가장 오래된 아키텍처 두통이다. 직렬화 시스템이 제네릭을 이해하지 못한다. 한 번도 이해한 적 없다. 타입 안전하고 데이터 기반인 이벤트 시스템을 만들려고 시도한 모든 개발자가 이 벽에 정면으로 부딪혔다.

사소한 불편이 아니다. 아키텍처 전체를 오염시키는 종류의 제한이다. 타입 안전성을 포기하거나, 보일러플레이트에 익사하거나, 아름다운 제네릭 설계가 Inspector에 절대 닿지 못한다는 걸 받아들여야 한다. 수년간 커뮤니티의 답은 "그냥 구체 클래스를 직접 작성해라"였다. 그런데 보일러플레이트가 100% 예측 가능하다면, 왜 사람이 쓰고 있는 건가?

보이지 않는 스파게티 코드에 작별을: 당신의 이벤트 시스템이 프로젝트를 망치고 있는 이유

TinyGiants
GES Creator & Unity Games & Tools Developer

메서드 이름 하나를 바꿨다. 딱 하나 — OnPlayerDiedOnPlayerDefeated로. 게임 디자이너가 표현을 부드럽게 해달라고 해서. Play를 눌렀다. 아무 일도 안 일어난다. 컴파일 에러도 없다. 경고도 없다. Inspector에서 UnityEvent로 연결되어 있던 씬 오브젝트 열 개가 그냥... 조용히 멈췄다. 3일 뒤 QA가 보고하거나, 더 최악의 경우 유저가 직접 발견하기 전까지는 알 수가 없다.

익숙한 상황이라면, 축하한다 — 보이지 않는 스파게티 코드를 만난 것이다. IDE에서 안 보이고, 컴파일러 경고도 안 뜨고, 의존성 그래프에도 나타나지 않는 종류의 기술 부채다. 그저 거기 앉아서 최악의 타이밍에 터지길 기다리고 있을 뿐이다.

이건 실력 문제가 아니다. 아키텍처 문제다. 그리고 대부분의 Unity 개발자가 인정하고 싶어하는 것보다 훨씬 흔한 일이다.