#22: Multi-cell(Jigsaw) Inventory System
Coding/Devlog

#22: Multi-cell(Jigsaw) Inventory System

Multi Cell Inventory

  • 디아블로나 이스케이프 프롬 타르코프처럼 인벤토리가 2차원 모눈종이 상에 아이템을 보관하는 방식을 의미한다.
  • 사진 출처
  • 보통 이런 인벤토리 방식은 난이도와 리얼리티를 더해준다.

Inventory 구성 요소

  • InventoryComponent : UActorComponent
    • 메인 캐릭터에 붙어 있는 액터 컴포넌트.
    • 인벤토리의 논리적 기능 (아이템 추가, 제거 등)을 담당한다.
  • InventoryWidget : UUserWidget
    • 인벤토리 UI에 해당하는 위젯
  • InventoryGridWidget : UUserWidget
    • 인벤토리 UI에서 아이템 그리드를 그리는 위젯
  • ItemWidget : UUserWidget
    • InventoryComponent가 가지고 있는 아이템들의 UI에 해당하는 위젯.
    • InventoryGridWidget의 자식으로 등록되어 있다.
  • ItemObject : UObject
    • 인벤토리에서 아이템에 대한 정보(아이템의 크기, 아이템의 그림 등)를 저장하는 UObject
  • Pickup : AActor
    • 월드에 있는 스폰되어 있는 아이템으로 플레이어가 주울 수 있다.
    • UObject를 가지고 있어서 인벤토리에 저장될 때, UObject를 던져준다.

Inventory의 저장 방식

  • 인벤토리가 가지고 있는 건 ItemObject의 배열이다. 멀티셀 방식이라고 해서 2차원 배열 자체를 저장하는 것이 아니다.
  • 2차원 배열을 저장하는 게 아니라서 아이템 제거가 간단하지만 아이템을 추가할 때 검사 과정의 효율성이 떨어지는 문제점이 있다. 최악의 경우, 인벤 가로 * 인벤 세로 * 넣는 아이템 가로 * 넣는 아이템 세로의 복잡도다.

Inventory의 논리 흐름

  • 획득
    1. Main CharacterPickup의 사정거리 안에 들어가서 아이템을 줍는다. Pickup은 실질적인 데이터에 해당하는 ItemObjectMain Character에게 던져준다.
    2. Main Character는 넘겨받은 ItemObjectInventory Component에게 줘서 Try Add Item 메소드를 실행한다.
    3. Try Add Item는 말그대로 함수 이름대로 인벤토리에 넣을 수 있나 한번 시도해보는 것이다. 블루 프린트는 다음과 같다.
    4. 핵심 함수는 Is Room Available이다. 저장 방식 문단에서 말했듯 이 함수가 그렇게 효율적이진 않다. Is Room Available의 작동 방식을 의사코드로 작성하면 다음과 같다.
      for left_top in inven_cells:
       for x in range(left_top, left_top + item.width):
           for y in range(left_top, left_top + item_height):
               for reserved_item in inven.items:
                   if (y, x) is in reserved_item's range:
                       return False
      return True
      반복문이 4개나 있어서 헷갈리는데, 첫번째 for문은 인벤토리의 모든 칸에 대해 이 칸이 넣으려는 아이템의 가장 왼쪽 위 귀퉁이가 될 수 있는지 검사하는 것이다. 나머지 3개 for문은 이 과정에서 이 아이템의 모든 칸이 다른 아이템의 어떤 칸과도 겹치지 않는다는 것을 검사하는 것이다.
  • 드랍
    1. Inventory Widget에서 드래그를 해서 Drop한다. Drag and Drop Operation을 통해 드래그 드랍 이벤트를 실행한다.
    2. Inventory Widget의 월드 포지션 앞에 ItemObject에 저장된 Pickup Class를 이용해 Actor를 생성한다.

결과