프로젝트를 앞으로 더 개선하고 업데이트 할 예정이지만 미리 앞서 캠프 기간동안의 내용을 모두 정리해두려고 한다.
정리 후 새로 코드 개선 및 업데이트는 새로운 이름으로 작성할 예정이다.
"그 세상에서 살아남는 법"은 생존과 포스트 아포칼립스를 결합하여 만든 게임으로 바이러스로 인해 무너진 세상에서 사용자에게 절망적인 상황에서의 생존 경험을 제공하는 게임입니다.
해당 게임은 PC와 모바일 환경에서도 즐겨 할 수 있도록 제작되었습니다.
깃 허브 사용 전략 : 개발자가 실수로라도 개발 또는 메인 브랜치에 push하는 것을 막고자
브랜치 프로텍션 룰을 적용 하였습니다.
작업해야 할 브랜치는 스크립트 통합 브랜치로부터 분기하여 생성했습니다.
분기된 브랜치는 헷갈리지 않도록 컨벤션을 두었으며 담당자에 이니셜 + 기능으로 만듭니다.
분기된 브랜치들은 통합 브랜치(개발 또는 메인)에 머지하기 위해서 반드시 풀 리퀘스트를 해야만 합니다. 이를 통해 실수로 병합하는 것을 방지했습니다.
또한 코드 리뷰도 강제함으로써 해당 코드에 실수가 없는지 확인하고,
병합하는 코드에 대해서 안정성을 확보 했습니다.
가장 먼저 저희가 지라를 사용한 이유로는 팀원들이 현재 작업하고 있는 내용을 한 눈에 파악하고 어떠한 작업을 완료하고 붕 뜨는 기간을 주지 않게 하기 위함이였습니다.
실제로도 백로그를 통해 스프린트를 관리하면서 해당 이슈티켓들에 대해 컨벤션을 만들고 깃 허브와 연동하여 사용하였습니다.
최종적으로 슬랙에 깃허브와 지라를 연동하여 해당 기능 담당자가 풀 리퀘스트를 요청하거나 새로운 이슈 티켓을 만들 때 알림을 오게 만듦으로써 바로 확인이 가능해 일에 효율성을 높일 수 있었습니다.
게임을 실행하면 타이틀 씬이 실행 되는데 여기에서 필요한 매니저들을 로드합니다.
이 후 터치를 통해 메인 씬으로 넘어가는데 이 과정 중에 비동기로 로딩 씬이 로드됩니다.
로딩씬에서 받은 데이터를 토대로 다음 씬 비동기 로드를 진행합니다.
빌드된 게임은 위와 같이 진행되지만,
현재 변경 중인 구조는 다음과 같습니다.
멀티 씬 체제를 채택해서 기존 사용하던 싱글톤 형태에 매니저들을
전부 없애고 이벤트 기반 프로그래밍을 활용해 매니저들에게 접근하여 사용합니다.
씬을 좀 더 설명을 해드리면 "이니셜라이저 씬" 을 제외한
모든 씬은 어드레서블로 지정 되고, 에셋 레퍼런스형태로 SO에서 관리됩니다.
이니셜라이저 씬에서 기본적으로 필요한 퍼시스턴트 매니저 씬을 비동기로 로드한 후
다음 씬 SO 데이터를 씬 매니저에게 전달하면서 로드를 요청합니다.
이 후 매니저는 로딩 스크린을 활성화 할지 개발자로부터 응답 받고 비동기로 로드합니다.
이제 이 영구적인 씬에서 각 게임오브젝트들은 하나에
컴포넌트만 들고 매니저 역할을 수행하게 됩니다.
실제 매니저에게 접근하기 위해서 SO를 이벤트 채널 즉 매개체로 활용합니다.
이와 같은 멀티 씬 체제로 보다 효율적인
오브젝트 및 씬 관리를 할 것입니다.
플레이어는 기본적으로 FSM을 사용합니다.
그리고 입력으로부터 컨트롤 할 수 있는 PlayerController와 실질적 플레이어 모델로서 상호작용 할 데이터 컨테이너 Stat Controller와 Player, Inventory 등이 존재합니다.
플레이어는 실질적인 행동 (Action)으로부터
어떤 상태에서에 전환까지 FSM에서 처리합니다.
하지만 이로 인해서 FSM에 대한 복잡도가 증가해 시각화가 필요 했습니다.
Enemy AI의 행동 판단은 Behavior Tree를 사용하며,
노드의 순서에 따라 3가지의 상태를 반환하여 행동을 결정합니다.
UI Builder를 사용하여 만든 Editor View를 이용하여 노드들을 조립하여 해당 Enemy가 사용할 행동의 AI를 만듭니다.
Enemy는 NavMeshAgent를 이용하여 목적지를 할당하고, 올바른 경로를 찾아 이동합니다.
Enemy의 체력, 공격력 등 능력치의 기반 베이스인 Stat Controller와 사망시 루팅이 가능하게 만들어주는 Looting,
각종 SFX를 재생할 Sound로 이루어져있습니다.
인벤토리, 상자, 제작대는 Inventory 스크립트를 기본적으로 가지고 동작을 하며 각각의 슬롯들은
ItemSlot 스크립트를 가지고 있어 Manager_Inventory에서 전달받은 데이터로 플레이어한테 정보를 전달합니다.
Manager_Inventory의 경우 Player한테 동작하도록 만들어 인벤토리, 상자, 제작대의 Data들은 관리하고 전달하는 데 사용됩니다.
제작대는 조합 아이템의 Data를 추가적으로 가지고 있어야 하기 때문에 Inventory 스크립트 외에 추가적으로
Crafting 스크립트를 가지게 만들었습니다.
유니티의 새로운 인풋 시스템은 다양한 입력 장치를 지원하기 위해 매우 유연하게 설계되었습니다. 그러나 모바일 플랫폼에서 UI 요소와의 연동, 특히 온-스크린 조이스틱을 구현하는 과정에서 몇 가지 어려움에 부딪혔습니다.
초기에 저희는 유니티가 기본적으로 제공하는 온-스크린 조이스틱을 사용했습니다. 하지만 곧 이 기본 조이스틱이 저희가 원하는 사용자 경험과 맞지 않다는 것을 깨달았습니다. 저희에게 필요한 것은 훨씬 더 맞춤화된 조작감과 기능을 제공하는 조이스틱이었습니다.
이 문제를 해결하기 위해, 저희는 온-스크린 컨트롤을 상속받아 커스텀 조이스틱을 개발하기로 결정했습니다. 이 과정에서 전략 패턴을 적용하여, 다양한 조이스틱 타입과 기능을 손쉽게 추가하고 변경할 수 있는 구조를 만들었습니다.
이를 통해, 저희는 사용자가 게임 내에서 다양한 입력 방식을 원활하게 전환할 수 있도록 지원하는 동시에, 게임의 몰입감을 해치지 않는 사용자 인터페이스를 제공할 수 있었습니다.
초기에 저희 프로젝트에서는 FSM을 구현하기 위해 하나의 스크립트 내에 모든 상태를 직접 정의하고 관리하는 방식을 사용했습니다.
이 접근 방식은 간단한 시스템에서는 잘 작동할 수 있지만,
프로젝트가 복잡해질수록 상태 관리가 점점 더 어려워졌습니다.
상태가 늘어날수록 스크립트의 복잡도가 증가하고, 유지보수가 힘들어지며, 팀원 간의 협업도 어려워지는 문제가 발생했습니다.
이러한 문제를 해결하기 위해 저희는 FSM의 구현 방식을 재고하기로 결정했습니다.
그리고 그 해결책으로 SO를 활용한 시각화 가능한 FSM으로 전환했습니다.
저희는 Transition Table SO, Condition SO, State SO, Action SO와 같은 구조를 도입하여 FSM의 각 요소를 모듈화하고,
Unity 에디터 내에서 직접적으로 상태를 시각화하고 관리할 수 있게 되었습니다.
이러한 접근 방식은 여러 가지 이점을 가져다주었습니다.
먼저, 상태 관리가 훨씬 용이해졌습니다. 각 상태와 조건, 행동을 별도의 SO로 관리함으로써 복잡성을 대폭 줄일 수 있었고, 코드에 상태가 추가되지 않으니 가독성도 향상되었습니다.
또한, Unity 에디터에서 시각적으로 FSM을 구성할 수 있게 되면서, 팀원들 간의 협업과 커뮤니케이션도 크게 개선되었습니다.
이 변화를 통해 저희는 FSM의 유연성과 확장성을 크게 향상시킬 수 있었습니다. 새로운 상태나 조건, 행동을 추가하는 것이 훨씬 간단해졌고, 변경 사항을 적용하는 데 드는 시간도 대폭 줄어들었습니다.
단점을 보안하기 위해 각 액션을 독립된 클래스로 분할하여 가독성과 관리 용이성을 향상시키고
Scriptable Object를 활용하여 중복되는 데이터 방지와 인스펙터 창을 통한 노드 구조 확인을 용이하게 했습니다.
UI Builder를 이용해, 시각적으로 행동트리를 구성하고 확인할 수 있는 에디터를 만들어 노드 간의 관계와 흐름을 쉽게 파악할 수 있게 하였습니다.
또한 OnStart, OnUpdate, OnStop 메소드를 포함한 FSM 구조를 적용해 노드의 순환을 개선했습니다. 이는 특정 상태에서 필요한 초기 설정을 한 번만 실행하게 하여 유지보수성을 더욱 강화했습니다.
에디터를 사용하여 노드 구조 및 흐름을 한눈에 확인 가능하여, 가독성 및 개발 효율성이 크게 향상되었고 독립적인 액션 클래스 설계를 통해 유지보수성 개선되었습니다. 그리고 FSM 순환 요소 도입으로 일회성 값 관리 및 수정 용이성이 증가 하였습니다.
오클루전 컬링은 카메라 시야에 보이지 않는 객체들을 렌더링에서 제외시키는 기술입니다.
이를 통해 불필요한 렌더링 작업을 줄이고, 따라서 Batching 과정도 최적화할 수 있었습니다.
오클루전 컬링을 적용한 결과, 저희는 성능 향상을 경험할 수 있었습니다. Batching 작업이 크게 감소함으로써 프레임 속도가 상당히 향상되었고, 전반적인 게임 플레이 경험도 더욱 부드러워졌습니다.
앞으로 이 부분은 멀티씬으로 나눠 레벨에 대한 정적 배칭과 같은 부분을 더 세세하게 다뤄서 배치수를 더 줄여 600 ~ 800정도로 만드는 것이 목표입니다.
Enemy AI의 감지 시스템을 개선하여 시야각(Field Of View, FOV)과 소리 감지(Sound Detect)를 통해 더 현실적이고 자연스러운 반응을 구현하였습니다.
이 과정에서 Angle 함수의 사용은 내부적으로 제곱근 계산과 ArcCos 계산을 필요로 하며, 이는 연산 비용이 상대적으로 높아 성능에 영향을 줄 수 있습니다. 반면, Dot 함수는 벡터의 곱셈과 덧셈만을 활용하기 때문에 연산 비용이 낮기에 보다 효율적입니다.
특히 Enemy AI가 행동 트리를 이용해 빈번하게 플레이어를 감지하고 판단하는 데 있어서, Angle이 아닌 Dot을 사용함으로써 성능 최적화를 달성할 수 있었습니다. 이러한 최적화는 AI 처리 속도를 개선하여 게임의 전반적인 반응성과 성능을 향상시켰습니다.
실제 유저들의 피드백을 통해 게임의 질을 향상시키고, 무엇보다 유저들이 원하는 게임을 제작하는 데에 큰 도움이 되었습니다.
유저테스트를 통해 게임 플레이 타임, 만족도 등 다양한 수치화된 자료를 수집하였습니다.
이 자료들은 객관적이며 신뢰할 수 있는 데이터로, 저희가 게임을 수정하고 개선하는데에 있어서 빠르고 정확한 판단을 내릴 수 있게 도와주었습니다.
유저테스트 단계를 통해 '게임 제작'이라는 과정이 단순히 제작자의 창작 활동만이 아니라, 실제로 게임을 즐기는 유저들과의 소통과 협업을 통해 완성된다는 것을 깨달았습니다.
이는 우리의 게임이 최종적으로 어떻게 받아들여지는지, 그리고 어떤 가치를 가지게 되는지를 결정짓는 중요한 요소라고 생각할 수 있었습니다.