Unity 최종 프로젝트 - Temporary Shutdown

이준호·2024년 3월 13일
0
post-custom-banner

📌 Unity 최종 프로젝트



프로젝트를 앞으로 더 개선하고 업데이트 할 예정이지만 미리 앞서 캠프 기간동안의 내용을 모두 정리해두려고 한다.
정리 후 새로 코드 개선 및 업데이트는 새로운 이름으로 작성할 예정이다.

📌 최종 프로젝트 정리

➔ 프로젝트 개요

  • "그 세상에서 살아남는 법"은 생존과 포스트 아포칼립스를 결합하여 만든 게임으로 바이러스로 인해 무너진 세상에서 사용자에게 절망적인 상황에서의 생존 경험을 제공하는 게임입니다.

  • 해당 게임은 PC와 모바일 환경에서도 즐겨 할 수 있도록 제작되었습니다.











➔ 사용된 기술 스택

GitHub

  • 깃 허브 사용 전략 : 개발자가 실수로라도 개발 또는 메인 브랜치에 push하는 것을 막고자
    브랜치 프로텍션 룰을 적용 하였습니다.

  • 작업해야 할 브랜치는 스크립트 통합 브랜치로부터 분기하여 생성했습니다.
    분기된 브랜치는 헷갈리지 않도록 컨벤션을 두었으며 담당자에 이니셜 + 기능으로 만듭니다.

  • 분기된 브랜치들은 통합 브랜치(개발 또는 메인)에 머지하기 위해서 반드시 풀 리퀘스트를 해야만 합니다. 이를 통해 실수로 병합하는 것을 방지했습니다.
    또한 코드 리뷰도 강제함으로써 해당 코드에 실수가 없는지 확인하고,
    병합하는 코드에 대해서 안정성을 확보 했습니다.






Jira

  • 가장 먼저 저희가 지라를 사용한 이유로는 팀원들이 현재 작업하고 있는 내용을 한 눈에 파악하고 어떠한 작업을 완료하고 붕 뜨는 기간을 주지 않게 하기 위함이였습니다.

  • 실제로도 백로그를 통해 스프린트를 관리하면서 해당 이슈티켓들에 대해 컨벤션을 만들고 깃 허브와 연동하여 사용하였습니다.

  • 최종적으로 슬랙에 깃허브와 지라를 연동하여 해당 기능 담당자가 풀 리퀘스트를 요청하거나 새로운 이슈 티켓을 만들 때 알림을 오게 만듦으로써 바로 확인이 가능해 일에 효율성을 높일 수 있었습니다.











➔ 클라이언트 구조

Scene

Before

  • 게임을 실행하면 타이틀 씬이 실행 되는데 여기에서 필요한 매니저들을 로드합니다.

  • 이 후 터치를 통해 메인 씬으로 넘어가는데 이 과정 중에 비동기로 로딩 씬이 로드됩니다.
    로딩씬에서 받은 데이터를 토대로 다음 씬 비동기 로드를 진행합니다.

  • 빌드된 게임은 위와 같이 진행되지만,
    현재 변경 중인 구조는 다음과 같습니다.

After

  • 멀티 씬 체제를 채택해서 기존 사용하던 싱글톤 형태에 매니저들을
    전부 없애고 이벤트 기반 프로그래밍을 활용해 매니저들에게 접근하여 사용합니다.

  • 씬을 좀 더 설명을 해드리면 "이니셜라이저 씬" 을 제외한
    모든 씬은 어드레서블로 지정 되고, 에셋 레퍼런스형태로 SO에서 관리됩니다.

  • 이니셜라이저 씬에서 기본적으로 필요한 퍼시스턴트 매니저 씬을 비동기로 로드한 후
    다음 씬 SO 데이터를 씬 매니저에게 전달하면서 로드를 요청합니다.

  • 이 후 매니저는 로딩 스크린을 활성화 할지 개발자로부터 응답 받고 비동기로 로드합니다.

  • 이제 이 영구적인 씬에서 각 게임오브젝트들은 하나에
    컴포넌트만 들고 매니저 역할을 수행하게 됩니다.
    실제 매니저에게 접근하기 위해서 SO를 이벤트 채널 즉 매개체로 활용합니다.

  • 이와 같은 멀티 씬 체제로 보다 효율적인
    오브젝트 및 씬 관리를 할 것입니다.






Player

  • 플레이어는 기본적으로 FSM을 사용합니다.

  • 그리고 입력으로부터 컨트롤 할 수 있는 PlayerController와 실질적 플레이어 모델로서 상호작용 할 데이터 컨테이너 Stat Controller와 Player, Inventory 등이 존재합니다.

  • 플레이어는 실질적인 행동 (Action)으로부터
    어떤 상태에서에 전환까지 FSM에서 처리합니다.

  • 하지만 이로 인해서 FSM에 대한 복잡도가 증가해 시각화가 필요 했습니다.






Enemy

  • Enemy AI의 행동 판단은 Behavior Tree를 사용하며,
    노드의 순서에 따라 3가지의 상태를 반환하여 행동을 결정합니다.

  • UI Builder를 사용하여 만든 Editor View를 이용하여 노드들을 조립하여 해당 Enemy가 사용할 행동의 AI를 만듭니다.

  • Enemy는 NavMeshAgent를 이용하여 목적지를 할당하고, 올바른 경로를 찾아 이동합니다.

  • Enemy의 체력, 공격력 등 능력치의 기반 베이스인 Stat Controller와 사망시 루팅이 가능하게 만들어주는 Looting,
    각종 SFX를 재생할 Sound로 이루어져있습니다.






Inventory

  • 인벤토리, 상자, 제작대는 Inventory 스크립트를 기본적으로 가지고 동작을 하며 각각의 슬롯들은
    ItemSlot 스크립트를 가지고 있어 Manager_Inventory에서 전달받은 데이터로 플레이어한테 정보를 전달합니다.

  • Manager_Inventory의 경우 Player한테 동작하도록 만들어 인벤토리, 상자, 제작대의 Data들은 관리하고 전달하는 데 사용됩니다.
    제작대는 조합 아이템의 Data를 추가적으로 가지고 있어야 하기 때문에 Inventory 스크립트 외에 추가적으로
    Crafting 스크립트를 가지게 만들었습니다.






Item Data

  • Enum 타입을 사용하여 각 아이템 구분할 수 있도록 만들었습니다. 또한 부모 DataSO를 기반으로 모든 아이템을 받을 수 있도록 만들어 간단하게 아이템을 추가하고 제거할 수 있습니다.





Interaction

  • Enemy가 Player를 판단하는 구조는 소리가 발생했는지 체크를 하고, 감지범위 안에 있는지 확인합니다. 이 때, 소리가 발생 한 상황이고,감지범위 안에 있으면 바로 추적을 합니다.
    아니라면 시야각 안에 있는지 확인하고 Enemy와 Player사이에 장애물이 있는지 체크를 하여 최종적으로 추적을 하게 됩니다.

  • 데미지 적용 판단으로는
    애니메이션이 30%이상 진행되고,
    공격 범위안에 있는지 확인한뒤
    시야각안에 있고 장애물이 없다면 데미지가 적용됩니다.

  • 루팅 상호작용 판단으로는
    루팅 범위안에 오브젝트가 있는지 확인하고, 레이어가 "옵스타클" 인지 확인합니다, 그리고 타입에 따라 디그, 루팅, 픽업 으로 진행됩니다.










➔ 트러블 슈팅 및 최적화

Joystick Variation

  • 유니티의 새로운 인풋 시스템은 다양한 입력 장치를 지원하기 위해 매우 유연하게 설계되었습니다. 그러나 모바일 플랫폼에서 UI 요소와의 연동, 특히 온-스크린 조이스틱을 구현하는 과정에서 몇 가지 어려움에 부딪혔습니다.

  • 초기에 저희는 유니티가 기본적으로 제공하는 온-스크린 조이스틱을 사용했습니다. 하지만 곧 이 기본 조이스틱이 저희가 원하는 사용자 경험과 맞지 않다는 것을 깨달았습니다. 저희에게 필요한 것은 훨씬 더 맞춤화된 조작감과 기능을 제공하는 조이스틱이었습니다.

  • 이 문제를 해결하기 위해, 저희는 온-스크린 컨트롤을 상속받아 커스텀 조이스틱을 개발하기로 결정했습니다. 이 과정에서 전략 패턴을 적용하여, 다양한 조이스틱 타입과 기능을 손쉽게 추가하고 변경할 수 있는 구조를 만들었습니다.

  • 이를 통해, 저희는 사용자가 게임 내에서 다양한 입력 방식을 원활하게 전환할 수 있도록 지원하는 동시에, 게임의 몰입감을 해치지 않는 사용자 인터페이스를 제공할 수 있었습니다.






FSM

  • 초기에 저희 프로젝트에서는 FSM을 구현하기 위해 하나의 스크립트 내에 모든 상태를 직접 정의하고 관리하는 방식을 사용했습니다.

  • 이 접근 방식은 간단한 시스템에서는 잘 작동할 수 있지만,
    프로젝트가 복잡해질수록 상태 관리가 점점 더 어려워졌습니다.

  • 상태가 늘어날수록 스크립트의 복잡도가 증가하고, 유지보수가 힘들어지며, 팀원 간의 협업도 어려워지는 문제가 발생했습니다.

  • 이러한 문제를 해결하기 위해 저희는 FSM의 구현 방식을 재고하기로 결정했습니다.
    그리고 그 해결책으로 SO를 활용한 시각화 가능한 FSM으로 전환했습니다.

  • 저희는 Transition Table SO, Condition SO, State SO, Action SO와 같은 구조를 도입하여 FSM의 각 요소를 모듈화하고,
    Unity 에디터 내에서 직접적으로 상태를 시각화하고 관리할 수 있게 되었습니다.

  • 이러한 접근 방식은 여러 가지 이점을 가져다주었습니다.

  • 먼저, 상태 관리가 훨씬 용이해졌습니다. 각 상태와 조건, 행동을 별도의 SO로 관리함으로써 복잡성을 대폭 줄일 수 있었고, 코드에 상태가 추가되지 않으니 가독성도 향상되었습니다.

  • 또한, Unity 에디터에서 시각적으로 FSM을 구성할 수 있게 되면서, 팀원들 간의 협업과 커뮤니케이션도 크게 개선되었습니다.

  • 이 변화를 통해 저희는 FSM의 유연성과 확장성을 크게 향상시킬 수 있었습니다. 새로운 상태나 조건, 행동을 추가하는 것이 훨씬 간단해졌고, 변경 사항을 적용하는 데 드는 시간도 대폭 줄어들었습니다.






Behavior Tree

  • 초기에 모든 행동(Action)을 단일 스크립트에서 구현하다 보니, 프로젝트 규모가 커짐에 따라 코드의 복잡성이 증가하고 가독성이 떨어지는 문제점이 발생하였습니다.
    이로 인해 각 액션의 확인이 어려워지고 유지보수가 힘들어졌습니다.

  • 단점을 보안하기 위해 각 액션을 독립된 클래스로 분할하여 가독성과 관리 용이성을 향상시키고

  • Scriptable Object를 활용하여 중복되는 데이터 방지와 인스펙터 창을 통한 노드 구조 확인을 용이하게 했습니다.

  • UI Builder를 이용해, 시각적으로 행동트리를 구성하고 확인할 수 있는 에디터를 만들어 노드 간의 관계와 흐름을 쉽게 파악할 수 있게 하였습니다.

  • 또한 OnStart, OnUpdate, OnStop 메소드를 포함한 FSM 구조를 적용해 노드의 순환을 개선했습니다. 이는 특정 상태에서 필요한 초기 설정을 한 번만 실행하게 하여 유지보수성을 더욱 강화했습니다.

  • 에디터를 사용하여 노드 구조 및 흐름을 한눈에 확인 가능하여, 가독성 및 개발 효율성이 크게 향상되었고 독립적인 액션 클래스 설계를 통해 유지보수성 개선되었습니다. 그리고 FSM 순환 요소 도입으로 일회성 값 관리 및 수정 용이성이 증가 하였습니다.






Occlusion Culling

  • 오클루전 컬링은 카메라 시야에 보이지 않는 객체들을 렌더링에서 제외시키는 기술입니다.

  • 이를 통해 불필요한 렌더링 작업을 줄이고, 따라서 Batching 과정도 최적화할 수 있었습니다.

  • 오클루전 컬링을 적용한 결과, 저희는 성능 향상을 경험할 수 있었습니다. Batching 작업이 크게 감소함으로써 프레임 속도가 상당히 향상되었고, 전반적인 게임 플레이 경험도 더욱 부드러워졌습니다.

  • 앞으로 이 부분은 멀티씬으로 나눠 레벨에 대한 정적 배칭과 같은 부분을 더 세세하게 다뤄서 배치수를 더 줄여 600 ~ 800정도로 만드는 것이 목표입니다.






Enemy Detect Type

  • Enemy AI의 감지 시스템을 개선하여 시야각(Field Of View, FOV)과 소리 감지(Sound Detect)를 통해 더 현실적이고 자연스러운 반응을 구현하였습니다.

  • 이 과정에서 Angle 함수의 사용은 내부적으로 제곱근 계산과 ArcCos 계산을 필요로 하며, 이는 연산 비용이 상대적으로 높아 성능에 영향을 줄 수 있습니다. 반면, Dot 함수는 벡터의 곱셈과 덧셈만을 활용하기 때문에 연산 비용이 낮기에 보다 효율적입니다.

  • 특히 Enemy AI가 행동 트리를 이용해 빈번하게 플레이어를 감지하고 판단하는 데 있어서, Angle이 아닌 Dot을 사용함으로써 성능 최적화를 달성할 수 있었습니다. 이러한 최적화는 AI 처리 속도를 개선하여 게임의 전반적인 반응성과 성능을 향상시켰습니다.











➔ 유저 피드백

  • 실제 유저들의 피드백을 통해 게임의 질을 향상시키고, 무엇보다 유저들이 원하는 게임을 제작하는 데에 큰 도움이 되었습니다.

  • 유저테스트를 통해 게임 플레이 타임, 만족도 등 다양한 수치화된 자료를 수집하였습니다.

  • 이 자료들은 객관적이며 신뢰할 수 있는 데이터로, 저희가 게임을 수정하고 개선하는데에 있어서 빠르고 정확한 판단을 내릴 수 있게 도와주었습니다.

  • 유저테스트 단계를 통해 '게임 제작'이라는 과정이 단순히 제작자의 창작 활동만이 아니라, 실제로 게임을 즐기는 유저들과의 소통과 협업을 통해 완성된다는 것을 깨달았습니다.

  • 이는 우리의 게임이 최종적으로 어떻게 받아들여지는지, 그리고 어떤 가치를 가지게 되는지를 결정짓는 중요한 요소라고 생각할 수 있었습니다.

profile
No Easy Day
post-custom-banner

0개의 댓글