🛠️ 오늘 한 작업

  • 드랍 아이템 프리팹과 관련 스크립트 작업을 시작했습니다.
  • DropItems를 만들어 바닥에 떨어진 아이템 묶음을 관리하도록 했습니다.
  • DropItemPicker를 만들어 플레이어 주변 드랍 아이템을 감지하고 줍는 흐름을 잡았습니다.
  • 드랍아이템작업으로 감지 루프와 거리순 정렬을 추가했습니다.
  • 중간 발표회 진행 방식에 대한 설명을 들었습니다.

 

📌 오늘 계획

오늘은 드랍 아이템 시스템을 시작하는 날로 잡았습니다.

오전에는 아침 스크럼을 진행하고, 드랍 아이템 프리팹과 관련 스크립트를 먼저 만들 계획이었습니다.

  • 드랍 아이템 프리팹 만들기
  • 드랍 아이템 관련 스크립트 만들기

오후에는 계속 드랍 아이템 관련 코드를 작성하고, 시간이 되면 인벤토리에서 아이템을 버리는 기능과 UI까지 생각해보려고 했습니다.

솔직히 오늘 안에 다 끝날 수 있을지는 잘 모르겠다고 생각했습니다. 그래도 일단 드랍 아이템 시스템의 기본 구조는 만들어야 했기 때문에, 가능한 부분부터 진행했습니다.

 

📦 드랍 아이템 구조 만들기

먼저 바닥에 떨어진 아이템 묶음을 관리하기 위해 DropItems 클래스를 만들었습니다.

드랍 아이템은 한 위치에 하나만 떨어지는 것이 아니라, 여러 아이템이 같이 묶여 있을 수도 있습니다. 그래서 스택 가능한 아이템과 스택 불가능한 아이템을 나누어 관리하도록 했습니다.

DropItems.cs - 스택형 / 비스택형 아이템 분리

private readonly Dictionary<int, int> countableItems = new();
private readonly Dictionary<int, List<ItemStack>> unCountalbeItems = new();

public bool IsEmpty => countableItems.Count == 0 && unCountalbeItems.Count == 0;

스택 가능한 아이템은 아이템 ID와 수량만 관리해도 되지만, 비스택형 아이템은 다르게 처리해야 했습니다.

예를 들어 무기나 장비는 같은 아이템 ID를 가지고 있어도 내구도나 UID가 다를 수 있습니다. 그래서 비스택형 아이템은 단순 수량으로 합치지 않고, ItemStack 자체를 리스트로 보관하는 방식으로 잡았습니다.

이 구조를 잡아두면 나중에 인벤토리에서 아이템을 버리거나, 바닥에 떨어진 장비를 다시 주울 때 기존 아이템 상태를 유지할 수 있을 것 같습니다.

 

⭐ 드랍 아이템 등급 표시

드랍 아이템에는 등급별 이미지를 표시하는 기능도 넣었습니다.

한 드랍 오브젝트 안에 여러 아이템이 들어 있을 수 있기 때문에, 그중 가장 높은 등급의 아이템을 기준으로 이미지를 바꾸는 방식입니다.

예를 들어 같은 드랍 오브젝트 안에 일반 아이템과 희귀 아이템이 같이 들어 있다면, 희귀 등급 이미지가 보이도록 처리했습니다.

아직 UI가 완성된 단계는 아니지만, 바닥에 떨어진 아이템의 가치를 어느 정도 시각적으로 보여줄 수 있는 시작점이 될 것 같습니다.

 

🔍 주변 드랍 아이템 감지

다음으로 플레이어 주변에 있는 드랍 아이템을 감지하는 DropItemPicker를 만들었습니다.

감지 반경, 감지 주기, 드랍 아이템 레이어를 설정해두고, 일정 시간마다 주변 드랍 아이템 목록을 갱신하는 방식으로 작업했습니다.

DropItemPicker.cs - 주변 드랍 아이템 감지

var cols = Physics.OverlapSphere(pos, scanRadius, dropLayerMask);

_sortedDrops.Clear();

for (int i = 0; i < cols.Length; i++)
{
    var drop = cols[i].GetComponentInParent<DropItems>();

    if (drop == null) continue;
    if (_sortedDrops.Contains(drop)) continue;

    _sortedDrops.Add(drop);
}

_sortedDrops.Sort((a, b) =>
{
    float da = (a.transform.position - pos).sqrMagnitude;
    float db = (b.transform.position - pos).sqrMagnitude;

    return da.CompareTo(db);
});

Physics.OverlapSphere를 사용해서 플레이어 주변의 콜라이더를 찾고, 그중 DropItems를 가진 오브젝트만 목록에 넣었습니다.

이후 플레이어와의 거리를 기준으로 가까운 순서대로 정렬했습니다. 이렇게 해두면 나중에 드랍 아이템 창을 열었을 때, 가까운 아이템부터 보여주는 식으로 확장할 수 있을 것 같습니다.

아직 드랍 아이템 UI 갱신까지 완전히 연결한 것은 아니지만, 근처 아이템 목록을 계속 갱신할 수 있는 기본 흐름은 잡았습니다.

 

🤲 근처 아이템 줍기

감지된 드랍 아이템을 인벤토리에 넣는 흐름도 작성했습니다.

스택형 아이템은 AddItem으로 인벤토리에 추가하고, 비스택형 아이템은 기존 ItemStack을 유지한 채 TryAddStack으로 넣도록 했습니다.

DropItemPicker.cs - 드랍 아이템 줍기 핵심 흐름

if (!inv.AddItem(itemId, count)) return;
drop.TakeStackable(itemId, count);

while (drop.TakeUnStackable(itemId, out var stack))
{
    if (!inv.TryAddStack(stack))
    {
        drop.AddUnStackable(stack);
        return;
    }
}

if (drop.IsEmpty)
{
    Destroy(drop.gameObject);
}

여기서 중요한 건 비스택형 아이템을 새로 생성하지 않고 기존 ItemStack을 유지하는 부분입니다.

장비나 무기처럼 내구도가 있는 아이템은 바닥에 떨어졌다가 다시 주워도 기존 상태가 유지되어야 합니다. 그래서 드랍 아이템에서도 지금까지 인벤토리와 장비창에서 고민했던 UID와 런타임 데이터 유지 문제가 그대로 이어졌습니다.

드랍 오브젝트 안의 아이템을 전부 주웠다면 해당 오브젝트를 제거하도록 했습니다. 아직은 기본 흐름이지만, 나중에 아이템 버리기와 줍기 기능을 연결하는 기반이 될 것 같습니다.

 

📌 오늘의 회고

오늘은 드랍 아이템 시스템을 시작한 날이었습니다.

처음에는 드랍 아이템 프리팹과 스크립트 정도만 만들면 될 것 같았는데, 막상 생각해보니 스택형 아이템과 비스택형 아이템을 나눠야 했습니다. 특히 비스택형 아이템은 내구도와 UID를 유지해야 해서 단순히 아이템 ID와 수량만으로 처리할 수 없었습니다.

지금까지 인벤토리와 장비창에서 고민했던 아이템 구조가 드랍 아이템 쪽에서도 그대로 이어졌습니다. 결국 아이템 시스템은 한 군데만 잘 만들면 끝나는 게 아니라, 줍기 / 버리기 / 장착 / 사용 / 표시까지 전부 연결된다는 걸 다시 느꼈습니다.

그리고 중간 발표회 설명도 들었습니다. 저번 프로토타입 때 발표를 맡아서 부담이 컸던 기억이 있어서, 이번에는 제가 발표자가 아니라는 점이 조금 다행이었습니다. 그래도 제가 맡은 기능이 많기 때문에, 발표자가 설명하기 쉽도록 제 파트도 정리해둘 필요는 있어 보입니다.

아직 드랍 아이템은 WIP 상태입니다. 오늘은 바닥 아이템을 담는 구조와 주변 아이템을 감지해 줍는 흐름을 만든 정도이고, 다음에는 아이템 버리기와 드랍 UI까지 이어서 봐야겠습니다.