Unity 최종 프로젝트 - 16

이준호·2024년 2월 5일
0

📌 Unity 최종 프로젝트



📌 프로젝트 설명

  • Project Name : 그 세상에서 살아남는 법 (How To Survive In That World)

  • Genre : Survive / Post Apocalypse

  • Introduction : 바이르서로 인해 모든것이 무너진 세상에서 살아가는 생존게임

  • Platform Plan : Mobile (Android & IOS)

  • Development Environment

    • Unity Version : 2022.3.16f1
    • Templates : 3D (Built-In)
    • IDE : Visual Studio, Rider
    • VSC : GitHub, Jira











📌 기술 정리

➔ Stat System

기술 도입의 이유

  • SO를 통해 관리하는 Stat System
  • 스탯은 기본적인 게임에 있어 가장 중요한 요소라고 할 수 있습니다.
  • HP, EXP, 각종 스태이터스 등 이를 효과적으로 관리하기 위해서는 체계적인 시스템과 커스터마이징 에디터가 있어야된다고 판단하였습니다.
  • SO로 시각화를 하고 이를 토대로 관리하는 스탯 시스템을 만들었습니다.

구현 방법 및 구성

  • Stat.cs : 기본적인 베이스 스탯(최상위 부모 클래스)

  • Attribute.cs : Stat의 파생 클래스로 CurValue/MaxValue와 같은 HP, EXP타입을 위한 클래스

  • Primary.cs : 스탯 포인트, 스킬 포인트, (Strength/Agility)와 같은 타입을 위한 클래스

  • StatController.cs : 위 스탯들을 저장하고 반환 관리하기 위한 컨트롤러

  • StatModifier.cs : 스탯들에 계산을 보다 효율적으로 관리하기 위한 스탯 수정자 (Operator Type)과 같은 것을 지정

  • StatDefinitionSO

    • 모든 스탯들(Stat, Attribute, Primary)에 토대가 되는 BaseSO
  • StatTableSO

    • 위 스탯들을 관리하기 위한 테이블로 한눈에 시각화 하기 위함
  • 상속 구조 설계를 활용해서 위와 같은 시스템을 구축했습니다.

  • 확장성과 재사용성을 매우 고려하고 안정성(Dictionary Key 값을 Enum 타입으로 사용 등)을 고려했습니다.

  • StatDefinitionSO에서 개발자는 시각화된 에디터를 활용하여 쉽게 스탯을 구성할 수 있습니다.






➔ Stat System Script

기술 도입의 이유

  • 스탯에 전략적인 관리가 필요하다 판단하여, 체계적인 스탯 시스템을 만들었습니다.

구현 방법 및 구성

  • Stat : 기본이 되는 스탯 시스템 (베이스 밸류, StatDefinitionSO 기반)

  • Attribute : Stat을 상속 받는 파생 클래스 - Hp, Exp와 같은 CurValue/MaxValue가 존재하는 속성을 위함

  • Priamry : Stat을 상속 받는 파생 클래스 - Stat Point, Primary Stat(Strength, Agility) 같은 주 스탯을 위함

  • Stat Controller

    • 스탯을 컨트롤 하는 스탯 컨트롤러
    • 스탯을 Dictionary를 통한 관리
      • Key (Enum : StatType), Valeue (Stat)
    • GetStat과 Get 프로퍼티 Stat를 통한 가지고 있는 스탯에 대해 접근할 수 있다.
  • Stat Modifier

    • 스탯 수정자
    • Magnitude (스탯을 수정할 수정 값)을 지니고 있다.
  • StatDefinitionSO

    • 스탯 정의 Scriptable Object
    • BaseValue(기본 값) 설정, Capcity(한계 값) 설정 가능
    • 모든 Stat(Attribute, Primary 포함) Definition에 의해 정의
  • StatTableSO

    • 스탯 정의(StatDefinition)들을 모아 놓은 테이블





➔ Stat System Editor

기술 도입의 이유

  • 체계적인 SO의 관리 및 테이블 관리를 위한 에디터의 생성

구현 방법 및 구성

  • StatCollectionEditor
    • StatTable을 더블 클릭하면 해당 에디터가 등장
    • Stat, Attribute, Priamry Tab에서 Creat를 누르면 해당하는 SO를 생성(StatDefinition)
  • StatTableSO
    • 이 테이블은 이제 내부적으로 Instantiate한 StatDefinitionSO를 지내게 된다.
    • 위 같은 사항으로 인해 실질적인 SO는 Project View에 존재하지 않는다 (Asset 자산으로 존재X)
    • 각 테이블은 여러 몬스터에 스탯테이블 또는 플레이어가 될 수 있다.
  • Stat 관리 수정
    • Enum Type SO에 추가 된다.
    • StatType을 지정해야만 한다. (Health, Exp와 같은)





➔ Inventory

기술 도입의 이유

  • 게임 내에서 사용자의 아이템, 장비, 자원 등을 효율적으로 관리할 수 있습니다. 이는 게임의 복잡도를 줄일 수 있습니다.
  • 사용자가 아이템을 쉽게 볼 수 있게 하며, 아이템을 사용하거나 장착하는 등의 작업을 더욱 간편하게 만듭니다.
  • 시스템을 잘 설계하면, 다른 게임이나 프로젝트에서도 코드를 재사용할 수 있습니다. 이는 개발 시간을 줄이고, 코드의 유지 관리를 간편하게 만듭니다.

구현 방법 및 구성

  • Inventory
    • Inventory 안에 만들어진 Slot들을 관리하고 Slot의 번호와 함수들을 사용하여 아이템의 위치를 변경하고 Data를 전달하는 식으로 만들었습니다.
    • Inventory는 아이템이 추가되었을 때 같은 아이템이 있는지 확인하고 있으면 아이템의 Stack을 늘리고 없으면 아이템을 추가하는 함수도 있습니다.
  • Slot
    • Slot에는 아이템의 Data를 가지고 있어 해당 Data를 가지고 인벤토리상에서 이미지와 수량을 나타낼 수 있다.
    • Slot에는 슬롯의 번호 아이템 추가, 제거, 초기화, 기본 셋팅, 최대수량체크의 함수들을 가지고 있어 해당 함수들을 Inventory안에서 사용해 아이템을 옮기거나 변경할 수 있도록 하였습니다.
    • Slot에는 마우스가 오브젝트를 클릭, 드래그, 드래그 놓기 등을 유니티 상에 이벤트를 이용해 여러 기능들을 만들었습니다. (드래그와 드랍을 통해 아이템을 변경할 수 있음)
  • UI_Inventory, UI_ChestInventory
    • 해당 스크립트를 만들어 아이템을 초기 셋팅을 하고 오브젝트를 끄면 Data들을 Manager_Inventory로 보내는 역할을 합니다.

개선해야 할 점

  • 아직 UI가 불친절해 사용자가 편히 사용하도록 변경해야 하고 예를 들어 아이템을 누르면 해당 아이템의 설명, 이름, 아이템 타입 등을 보여주는 창을 추가해야 할 것 같습니다.
  • 이벤트를 활용해서 인벤토리와 슬롯, 인벤토리 매니저, 드래그 슬롯이 스크립트에서 연결하는 게 아닌 특정 이벤트가 실행이 되면 작동하는 식으로 변경을 해야 할 거 같습니다. 이러한 이유를 사용하는 이유는 확장성을 증가시키고 코드의 가독성을 향상시켜 이러한 방향으로 개선해야 합니다.





➔ Input System

기술 도입의 이유

  • SO를 통한 싱글톤 없이 인풋을 전역으로 관리하는 Input System
  • 인풋 시스템을 사용할 때 플레이어에 속하거나 인풋 매니저(싱글톤)을 활용 했었습니다.
  • 하지만, 인풋 특성상 바뀔 내용이 크게 존재하지 않고 물리장치와 연결이 더 강하다는 것을 판단하여 실제 게임오브젝트에 인풋을 상주시킬 필요가 없다고 생각이 들었습니다.
  • 위와 같은 점으로 인해 에디터에서 수정이 가능하고 사용이 가능한 SO 방식을 채택하게 되었습니다.

구현 방법 및 구성

  • InputReaderSO는 C# Generate로 생성한 Input을 기반으로 하여 인터페이스를 구현합니다.
  • 해당 이벤트들을 실행할 객체를 SO로 상주시킴으로써 인스턴스화가 아닌 에셋(자산화)를 하여 어디서든 접근할 수 있는 하나에 자산 파일로 만들었습니다.
  • Player와 같은 인풋을 필요로 하는 스크립트에 SO를 연결만 시켜주면 해당하는 Input Event에 접근이 가능합니다.





➔ On Screen Control Customizing

기술 도입의 이유

  • Unity New Input System과 스크린 UI동기화를 위한 On-Screen Control 커스터마이징
  • 인풋 시스템은 유니티에서 크로스 플랫폼에 대응하기 위한 유연한 입력 시스템입니다. 당연하게도 모바일과에 연동을 위한 UI들과에 Override가 제공됩니다.
  • 하지만 기존 제공되는 On-Screen Joystick은 하나에 조이스틱이며 단일 이미지가 핸들로서 바리에이션이 존재하지 않는 조이스틱입니다.
  • 위 점을 보완하기 위해 바리에이션이 존재하는 조이스틱으로 커스터마이징 했습니다.

구현 방법 및 구성

  • Only Handle Type
  • Handle + Background Type
  • Direction + Background Type
  • Handle + Direction + Background Type (All)
  • 위와 같은 상태들을 다 따로 지니기 때문에 [전략(Startegy) 패턴]을 사용하여 처리 했습니다.
  • CurrentJoystick은 위 해당하는 타입에 따라서 자동으로 알맞는 행동을 취합니다.





➔ SingleTon Base

기술 도입의 이유

  • 싱글톤을 확장성 있게 사용하기 위해서 루트(부모) 클래스로 만들었습니다.

개선해야 할 점

  • 싱글톤을 확장성 있게 사용하기 위해서 루트(부모) 클래스로 만들었습니다.

  • 현재 싱글톤은 비동기 작업에 영향을 받지 않기 위해 lock으로 다중 접근을 막은 상태이지만,

  • 모든 싱글톤은 DontBeDestroyed 형태를 지니게 되는 구조로 설계 되어 있어 베이스를 두 개로 나누어 보다 확장성 있는 싱글톤으로 설계할 예정입니다.

  • 이러한 싱글톤 베이스를 만들면 더 유연하게 사용이 가능합니다. 싱글톤 베이스를 기반으로 한 싱글톤 패턴은 확정성 있는 설계를 가능하게 하고 이를 통해 서브 클래스를 만들어서 사용할 수 있습니다. 다중으로 접근한는 것을 제어 하면 데이터의 일관성을 보장할 수 있습니다.

  • 하지만 이러한 단점은 DontDestroyOnLoad를 사용하면 해당 객체가 씬 변경시에도 파괴되지 않아 계속 메모리에 남게 됩니다. 이로 인해 불필요한 메모리 사용이 발생할 수 있습니다. 또한 모든 싱글톤이 동일한 루트 클래스를 상속받게 만들면, 각 싱글톤이 다른 클래스를 상속받는 것을 제한하게 됩니다. 이로 인해 확장성이 제한될 수 있습니다.

  • 이를 해결하기 위해 필요 없어진 싱글톤 인스턴스는 적절히 파괴하여 메모리 누수를 방지해야 합니다. 싱글톤이 다른 클래스를 상속받는 것을 제한하는 문제는 인터페이스를 사용하여 해결할 수 있습니다. 인터페이스를 사용하면 다른 클래스의 기능을 확장하면서도 싱글톤의 특성을 유지할 수 있습니다.






➔ Manager Admin

기술 도입의 이유

  • 모든 매니저들을 한 곳에서 접근할 수 있게끔 싱글톤 처리를 했습니다.
  • 이러한 기술을 사용하면 접근성이 용이해지고 리소스 효율성이 좋아집니다. 싱글톤을 사용하여 어디서든지 쉽게 매니저에 접근할 수 있게 되어 코드의 흐름을 단순화 시킵니다. 또한 하나의 객체만을 생성하고 공유하므로 메모리 사용량을 최소화 시켜 효율적 입니다

개선해야 할 점

  • 모든 매니저가 전역 상태를 유지 하기 때문에 여러 부분에서 상태가 변경되기 쉽고 디버깅을 통한 버그를 찾기 어려울 수 있습니다.
  • 단점을 보안하기 위해 각 매니저들에 캡슐화하면 외부에서의 상태 변경을 최소화하고, 상태 변경을 제어할 수 있습니다.





➔ Finite State Machine

기술 도입의 이유

  • SO를 통해 관리하는 범용 Finite State Machine (유한 상태 머신)
  • 유한 상태 머신은 게임자체에도 적용될 수 있는 범용적인 관리 기법중 하나입니다.
  • 간단하게는 switch와 enum을 통한 관리도 존재하지만, 상태가 많아짐에 따라 결합도가 높아지고 복잡성이 증가합니다.
  • 특히 이는 한 눈에 파악하기 어렵게 만들고 어떻게 흘러가는지 흐름 구성을 개발자가 추 후 판단하기 힘듭니다.
  • 위 같은 점으로 인해 기본적으로 전략 패턴을 사용 하였고,
  • 눈으로 파악하기 편하게 비주얼라이저(시각화)가 필요하다 판단 SO로 구성하기로 마음 먹었습니다.

구현 방법 및 구성

  • 초기 전략으로는 상태(State)를 표현하는 각각에 액션(실질적인 행동)들로 구성 했습니다.

  • 그리고 상태에서 전환(Transition)을 하기 위한 조건(Condition)들을 추가적으로 구성 했습니다.

  • 이를 이제 시각화 하기 위해 SO로 전부 만들었고 이 과정에서 스크립트 템플릿이필요 하다고 판단

  • ActionSO와 ConditionSO 기본 구성이 존재하는 Template을 만들었습니다.

  • ActionSO

    • 상태를 구성하는 단위로 실질적인 행동입니다. (이동 연산, 이동 적용, 물리 연산, 애니메이션 등)
  • StateSO

    • ActionSO들이 모여 하나에 상태를 만듭니다.
      (예시 - Moving State => Apply Move, Calculate Move, Move Anim)
  • ConditionSO

    • bool 값을 반환하는 메서드를 지니며 From State에서 To State에 전환 조건을 구성합니다.
      • 예시) Idle => Moving - IsMoving이 True가 반환 될 경우 반대는 False
  • TransitionSO

    • 위 모든 SO를 관리하는 전환 테이블입니다.
      State -> State로 전환하는데 필요한 컨디션을 지정합니다.





➔ Behavior Tree

기술 도입의 이유

  • 유한 상태 기계(Finite State Machine)가 비교적 간단한 행동표현들을 하기에 매우 직관적이고 관리 유용한것은 분명합니다.
  • 하지만 Enemy의 경우는 플레이어가 직접 키를 누르며 조종하며 상호작용하는 것이 아닌, Enemy가 알아서 그 상황에 맞는 적절한 행동이 나오도록 하게 해야합니다.
  • 그러기 위한 상황에 따른 조건의 분기들이 규모가 커지면 커질수록 오히려 스파게티처럼 꼬이고 복잡해져서 유지보수성이 떨어지고 가독성이 나빠집니다. 계층적 유한 상태기계(Hierarchical Finite State Machine)을 사용하면 나아지긴 하지만 Enemy의 입장으로는 근본적인 문제에 대한 해결책은 되지 못합니다.
  • 하지만, BT를 사용하여 트리 구조를 계측적으로 잘 짜두어 이미지화를 시켜 그에 맞게 클래스들을 나눠 행동 로직들의 추가나 제거가 편리해져 유지보수에 용이합니다.
  • 또한 한번 잘 짜둔 트리 구조를 기반으로 파생형을 만들거나, 다른 행동들을 편하게 확장하는 것이 가능하여 BT를 선정하였습니다.

구현 방법 및 구성

  • EnemyAI.cs에서 생성자를 통해 트리 구조를 구성.

  • 만든 트리 구조를 BehaviorTreeRunner에 생성자의 파라미터에 할당하여 객체를 생성 후 .Operater 메소드로 트리를 순회

  • Behavior Tree Core (BT를 돌아가게 하는 베이스)

    • INode

      • 노드의 통일성을 위한 Interface
      • 노드의 상태를 반환하는 Evaluate() 메소드
    • BehaviorTreeRunner

      • Root Node를 실행시켜주는 Operate 메소드
      • 처음 BT를 실행시켜주는 역할.
    • DataContext

      • BT를 이용한 AI를 만들 때, 사용할 컴포넌트 및 데이터변수 저장소
  • Action Node (실제 행동을 정의하는 가장 말단의 Leaf)

    • Func를 사용하여 실제 행동을 정의.
  • Composite Node (자식 노드가 2개이상, 자식 노드들의 평가를 진행)

    • RandomSelector

      • 하위 노드중 하나를 랜덤으로 진행
    • Selector

      • 자식 노드의 상태가 Fauilure라면 다음 자식노드 진행. (OR)
    • Sequence

      • 자식 노드의 상태가 Success라면 다음 자식노드 진행. (AND)
  • Decorator Node (자식 노드를 1개만 가질 수 있다, 자식을 꾸며준다)

    • Inverter

      • 자식 노드의 상태를 반전
    • Repeat

      • 자식 노드의 상태에 상관없이 지정된 횟수 반복 후 Success반환
    • Succed

      • 자식의 상태에 상관없이 항상 성공을 반환
    • UntilFail

      • 자식 노드가 실패를 반환할 때까지 계속 재검사

개선해야 할 점

  • 현재 Action Node를 Func를 이용해 생성자에서 생성할 때, 실제 구현 Action을 메소드로 받아 실행하는 형식입니다.
  • 이러한 방식은 간단한 BT를 구성 할 때는 매우 편리하지만, 점점 노드들이 늘어나다 보니 가독성이 매우 안좋아졌습니다.
  • 그래서 각 Action Node들을 클래스로 각각 나누고 Scriptable Object를 이용하여 유지보수 및 가독성을 향상시키는 작업을 준비중입니다.





➔ UI Binding

기술 도입의 이유

  • UI 바인딩 기술을 사용하여 UI 의 Button, Text, Image 등 스크립트상으로 관리할 수 있게 만들어 두었습니다 .
  • 바인딩 기술을 사용한 이유는 코드상으로 관리를 할 수 있어 병합과정에서 만들어둔 구조가 풀어질 일이 없어 에러를 줄일 수 있고 , 어떠한 방식으로 작동하고 움직이는지 더 보기 편하 다는 장점이 있어 사용하였고 이러한 이유 덕분에 개발 시간을 절약할 수 있습니다 .

구현 방법 및 구성

  • UI_Base
    • Bind를 통해 Enum의 이름으로 Dictionary로 오브젝트들을 저장시킵니다. 그 다음 Get함수를 통해 자신이 사용하고 싶은 오브젝트를 가져와 동작할 수 있게 만들었습니다
    • UI_Base를 부모로 두고 스크립트를 만들어서 간단하게 사용이 가능합니다.
  • Util, Extensions
    • Util에서는 FindChild함수를 만들어 원하는 타입의 오브젝트를 하이어라키 창에서 찾아주고
    • Extensions에 FindChild로 확장 메서드를 만들어 gameObjcet에서 편리하게 사용할 수 있도록 만들었습니다.
  • Manager_UI
    • SetCanvas를 통해 Canvas에 기본 정보들을 셋팅합니다.
    • ShowPopupUI는 리소스 매니저를 통해 Prefab을 전달하여 해당 프리펩의 부모를 지정해주고 GameScene에 생성합니다.
    • ClosePopup을 통해 생성된 Pop_Up UI를 지울 수 있는 기능을 합니다.

개선해야 할 점

  • 너무 많은 바인딩을 수행하면 성능에 영향을 줄 수 있기 때문에 필요이상의 바인딩을 피하고 필요한 경우를 사용해야합니다.

  • 바인딩을 최소화하기 위해서는 빈번하게 업데이트를 하지 않아도 되는 바인딩은 맨 처음 한번만 바인딩을 실시하고 만약 지속적으로 업데이트를 해야하는 바인딩의 경우 업데이트의 빈도의 수를 줄이거나 데이터가 변경할 때에만 업데이트를 실시하도록 개선할 예정입니다.












📌 트러블 슈팅 정리

➔ Field Of View

기술 도입의 이유

  • Enemy AI를 구현하며, 그저 어떠한 감지 범위 안에 들어와 인식하는 것이 아닌 실제 시야로 확인하여 인식하는 느낌을 주어 현실성과 자연스러움을 위해서 시야각을 이용한 인식(Field Of View)를 사용했습니다.

구현 방법

  • ”Physics.OverlapSphere” 를 이용하여 Player의 LayerMas를 체크하여 해당 범위에 들어오면 값을 할당합니다.
  • 값이 할당 된다면, 조건문을 통해 예외를 방지하고 ‘player의 transform’과 ‘Enemy에서 player로 향하는 방향'을 변수에 각각 저장합니다.
  • Vector3.Dot을 통하여 ‘Enemy의 정면’과 ‘Enemy에서 플레이어로 향하는 방향’의 내적이 0.9 이상인지 확인합니다.
  • 조건문이 True라면, Enemy에서 Player까지의 거리를 구하고 그 거리만큼 Enemy의 위치에서 Player방향으로 Ray를 쏩니다.
  • Ray에 “Obstacle(장애물)” LayerMask가 걸리지 않는다면, Enemy에게 Player의 transform을 할당하여 추적 및 공격을 실행하게 합니다.

개선한 내용과 이유

  • 원래는 Vector3.Dot이 아닌 Vector3.Angle을 이용하여 각도를 이용해 판단을 했었습니다.
  • 그 이유로는 Angle함수은 내부적으로 “제곱근 계산과 ArcCos계산 등 연산 비용이 높습니다", Dot함수는 “곱셈 및 덧셈만을 사용하기에 비교적 연산 비용이 비교적 크지 않습니다.
  • Enemy의 AI가 Behavior Tree를 이용하고 Tree의 순회가 자주 일어나는데 그 중에서도, Player를 감지하고 판단하는 노드는 가장 자주 발생하는 노드중 하나입니다. 그렇기에 이 부분에서 최대한 최적화를 해주기 위해서 Angle -> Dot을 사용하게 되었습니다.





➔ Custom Editor

기술 도입의 이유

  • Field Of View를 이용한 범위 및 시야가 실제 잘 작동하는지 눈으로 확인할 수가 없어 어려움을 겪었습니다. 그 문제를 해결하기 위해 Custom Editor를 이용하여 눈으로 확인이 가능하도록 그려주기 위해서 사용했습니다.

구현 방법

  • 그려주는 코드를 짤 스크립트를 만들고 “Editor”를 상속시켜줍니다.
  • 클래스 위에 [CustomEditor(typeof(클래스 이름))] 이 에디터를 사용할 스크립트의 클래스 이름을 넣어 해당 타입으로 받아옵니다.
  • Scene View에서 GUI를 표시하는 함수인 “OnSceneGUI”를 사용합니다.
  • OnSceneGUI 메소드 안에 사용해야 할 정보들을 가져오고 Handles 클래스를 이용해 내부 메서드 color과 DiaWireArc를 이용해 색과 원(감지 범위)을 그려줍니다.
  • 시야각에 해당하는 값을 구해서 Handles.DrawLine을 이용해 시야각을 그려줍니다.
  • Enemy가 감지된 플레이어가 할당되어 있다면, DawLine을 이용해 Enemy에서 Player까지를 그려줍니다.

개선한 내용과 이유

  • 처음에는 Gizmos를 이용하여 그려서 확인하였는데, 2D 평면이 아닌 3D 입체적으로 밖에 그려지지 않아서 확인은 가능해도 불편함도 크고 따로 코드 관리하기도 불편하였습니다.
  • 그리하여 Custom Editor를 이용하여 따로 코드를 관리하여 유지보수도 편하고 2D 평면으로 그려져 다른 건물들과 걸리지 않게 확인이 가능하였습니다.





➔ 데이터 매니저 활용

도입 배경

  • 데이터 관리와 변환 효율을 높이기 위해 데이터 매니저와 CSV를 JSON으로 변환하는 스크립트를 도입

구현 과정

  • 단일 데이터 변환을 목표로 시작했으나, 중첩 데이터 처리가 필요했고, 필요성에 따라 isNested bool값을 추가하여 JSON 파일 생성 기능을 확장하였습니다.

개선점 및 개선 과정

  • JSON 데이터가 문자열로만 저장되는 문제를 발견, 데이터 타입 확인 및 변환을 위한 ConvertValue 메서드를 추가했습니다.
  • 데이터 매니저가 플레이어 데이터만 처리하던 구조를 아이템 데이터도 포함하도록 개선했습니다.

회고

  • 프로젝트를 통해 데이터 관리의 중요성, 복잡성을 이해하고, 유연한 코드 설계의 필요성을 깨달았습니다. 데이터 타입에 따른 처리 방식의 중요성도 알게 되었습니다.





➔ 애니메이터 활용

도입 배경

  • 기존에 알고있던 Layer, subState 활용보다 프로젝트에서 애니메이션의 복잡성을 줄이고, 필요한 애니메이션을 효율적으로 구성하고자 BlendTree를 활용하여 애니메이터를 설계하게 되었습니다. 애니메이터의 구성을 단순화할 수 있을 것으로 기대

구현 과정

  • 기본적인 Idle, Walk, Run 움직임을 개별 State로 구성하는 대신, BlendTree를 활용

미해결 문제점

  • 들어오는 input 값에 따라 블렌드 트리의 좌표값을 일치시켜 조이스틱의 움직임에 맞게 움직이려 했으나, 조이스틱을 살짝만 움직여도 값이 크게 변하는 문제를 발견

시도

  • 위의 문제점은 캐릭터의 애니메이션 전환(Walk에서 Run)이 보이지 않게 됨. 조이스틱으로 들어오는 입력값에 보간작업이 되어있지 않아서 생기는 문제점이라고 파악함. / 이를 해결하기 위해 보간작업을 통해 문제를 해결하려고 계획

회고

  • 이 프로젝트를 통해 애니메이션의 복잡성을 관리하는 방법과 BlendTree의 활용법에 대해 이해하게 되었습니다. 또한, 다른 팀원이 만들어준 기능을 분석하고 문제를 찾아 해결하는 과정에서 커뮤니케이션의 중요성을 깨달았습니다.





➔ 데이터 구조 설계

  • Runtime 데이터와 File 데이터를 어떻게 잘 구성해서 사용할 수 있을까 Data Architecture 설계에서 어떠식으로 구성을 해야할지 방향을 잡기 힘들었다. 머릿속에 그려지는 것과 실질적으로 Figma에 시각화 요소를 만들었음에도 불구하고 명확한 구조를 얻어내기가 어려웠습니다.
  • 위 구조를 얻기 힘들었던 이유 중 하나는 File Data와 Runtime Data를 명확히 구분하지 않고 애매한 선에서 둘 데이터를 한번에 사용할려고 했기 때문이었습니다.
  • File Data (직렬화 된 데이터)
  • Runtime Data (File Data를 기반으로 재설정)
  • 위와 같은 시각으로 접근하니 데이터 구조를 명확하게 구현할 수 있었습니다.
  • File Data로는 [Serializable]을 사용한 단순 float 또는 int, string과 같은 타입입니다.
  • 위 데이터를 역직렬화 시켜서 가져온 후
  • Runtime Data 에 제공하여 초기화하는 방식을 사용해 명확한 경계선을 구분하니 구조 설계가 명확해졌습니다.
  • 추 후 만든 스탯 시스템으로 인해 내용 변경 CSV, JSON과 같은 데이터로도 관리할 수 있지만 StatSO를 관리할 에디터를 커스터마이징 하여 관리하면 보다 적합하게 할 수 있다고 판단 했습니다.





➔ Item Stack

버그 내용

  • 인벤토리의 아이템을 추가하면 같은 아이템이고 아이템의 Data의 MaxStack이 2이상이면 아이템의 Stack이 쌓이는데 아이템이 추가 될수록 Stack의 값이 늘어나며 MaxStack이 되서 아이템을 추가하면 아이템이 MaxStack의 값을 가지고 추가됨

버그 이유

  • 아이템의 배열을 참조 형식으로 관리를 하니 값이 전달이 되는게 아니라 아이템의 주소가 전달이 되어 인벤토리의 ItemData의 내용이 아이템을 추가하는 함수의 Data와 연동이 되어 Stack의 값이 늘어나는 거였습니다.

해결 방법

  • ItemData클래스에 생성자를 만들어 내가 아이템을 추가할 때 생성자를 통해 new ItemData(아이템 Data) 이런식으로 새로운 Data를 보내 아이템이 Stack이 쌓이는 버그를 해결했다.





➔ 아이템 사라짐 버그

버그 내용

  • 아이템을 Drag and Drop을 하면 내가 원하는 위치의 슬롯으로 변경하지만. 같은 공간에 Drop을 실시하면 아이템이 사라지는 버그가 생겼다

버그 이유

  • 옮기고 싶은 Slot을 클리하면 Drag Slot이 활성화 되서 클릭한 Slot의 Data를 저장시켜 Drop한 위치의 슬롯에다가 Data를 전달하며 아이템을 옮기는데 똑같은 위치에 Drop을 하면 OnDrop이벤트에 Data충돌이 생겨 아이템의 Data가 사라졌다.

해결 방법

  • OnDrop 이벤트함수 안에 조건을 추가하여 현재 슬롯(this)이 DragSlot이 가지고있는 Slot과 같으면 함수를 실행을 안시키도록 해서 버그를 해결하였습니다.
profile
No Easy Day

0개의 댓글