🧠 오늘의 핵심 정리

  • feat: Inventory (WIP) 인벤토리 기능 추가 시작
  • Inventory / Item / ItemData(SO) 기본 구조 생성
  • UIInventory + ItemSlot으로 슬롯 UI 갱신 흐름 연결
  • 아이템 더블클릭 처리(Use/Equip) 뼈대 구현
  • 다음 단계: Player와 인벤토리 연결 + 드래그 앤 드롭 확장 준비

 

 

🧩 Inventory (WIP) 구현 시작

오늘은 최종 프로젝트에서 제가 맡게 될 핵심 파트인 인벤토리를 본격적으로 시작했습니다.

아직 WIP 단계라서 Player와 완전히 연결되진 않았고, 우선은 아이템 데이터(ScriptableObject) → 인벤토리 리스트 → UI 슬롯까지 흐름이 돌아가게 뼈대를 잡는 데 집중했습니다.

 

 

📦  Inventory.cs 추가 (아이템 추가/삭제/사용 이벤트)

Inventory는 MonoBehaviour로 만들고, 내부에 List<Item>을 들고 있도록 구성했습니다. 아이템이 변경될 때 UI가 갱신될 수 있도록 OnInventoryChanged 이벤트도 추가했습니다.

Inventory.cs (추가된 핵심 구조)

public class Inventory : MonoBehaviour
{
    public List<Item> items;
    public int maxSlots = 45;

```
public event Action OnInventoryChanged;

public List<ItemData> initialItem;

private void Awake()
{
    items = new List<Item>();
}

private void Start()
{
    foreach(var itemData in initialItem)
    {
        AddItem(itemData, 1);
    }
    OnInventoryChanged?.Invoke();
}

```

그리고 AddItem에서는 스택 가능 아이템이면 기존 스택을 우선 채우고, 남은 수량은 새 슬롯에 넣는 방식으로 처리했습니다. 슬롯 제한은 maxSlots(현재 45) 기준으로 막아두었습니다.

스택 처리 + 슬롯 추가 로직

public void AddItem(ItemData data, int amount)
{
    if (data.MaxStack > 1)
    {
        foreach (var item in items)
        {
            if (item.data == data)
            {
                int spaceLeft = data.MaxStack - item.currentAmount;
                int addAmount = Mathf.Min(amount, spaceLeft);
                item.currentAmount += addAmount;
                amount -= addAmount;
                if (amount <= 0) return;
            }
        }
    }

```
while (amount > 0 && items.Count < maxSlots)
{
    int addAmount = Mathf.Min(amount, data.MaxStack);
    items.Add(new Item(data, addAmount));
    amount -= addAmount;
}
OnInventoryChanged?.Invoke();
```

}

 

 

🧱  Item / ItemData(SO) 기본 데이터 구조 추가

아이템은 런타임에서 들고 다닐 Item 클래스와, 정적 데이터인 ItemData(ScriptableObject)로 분리했습니다.

Item에는 어떤 데이터인지(ItemData)현재 수량(currentAmount)만 들고 있어서 단순하게 시작할 수 있었습니다.

Item.cs

public class Item
{
    public ItemData data;
    public int currentAmount;

```
public Item(ItemData data, int amount)
{
    this.data = data;
    this.currentAmount = amount;
}
```

}

ItemData는 아이템 타입/등급/아이콘/스택/장착 가능 여부/더블클릭 액션/무기 타입/내구도/허기/갈증 회복 등 필요한 필드를 먼저 넓게 깔아두었습니다. (프로젝트 진행하면서 더 정리될 것 같습니다.)

ItemData.cs (SO + Enum)

public enum ItemType { Weapon, Armor, Consumable}
public enum WeaponType { Melee, Ranged}
public enum ItemGrade { Normal, Rare, Epic, Unique}
public enum EquipSlotType { None, Head, Body, Arm, Leg, Weapon}
public enum DoubleClickAction { None, Use, Equip}

[CreateAssetMenu(fileName ="NewItem", menuName ="Item")]
public class ItemData : ScriptableObject
{
[SerializeField] private int itemId;
[SerializeField] private string itemName;
[SerializeField] private ItemType itemType;
[SerializeField] private ItemGrade grade;
[SerializeField] private Sprite icon;

```
[SerializeField] private int maxStack;
[SerializeField] private bool isEquipable;
[SerializeField] private EquipSlotType equipSlotType;
[SerializeField] private DoubleClickAction doubleClickAction;

[SerializeField] private WeaponType weaponType;
[SerializeField] private int attackRange;

[SerializeField] private int durability;

[SerializeField] private int hungerRestoration;
[SerializeField] private int thirstRestoration;

public int MaxStack => maxStack;
public Sprite Icon => icon;
public DoubleClickAction DoubleClickAction => doubleClickAction;
```

}

 

 

🖥️  UIInventory + ItemSlot 연결 (슬롯 갱신)

UI 쪽은 UIInventoryInventory.OnInventoryChanged를 구독해서, 아이템 변경 시 자동으로 슬롯 UI를 업데이트하도록 구성했습니다.

UIInventory.cs

private void OnEnable()
{
    inventory.OnInventoryChanged += UpdateUI;
}

private void OnDisable()
{
inventory.OnInventoryChanged -= UpdateUI;
}

public void UpdateUI()
{
for(int i = 0; i < slots.Length; i++)
{
if(i < inventory.items.Count)
{
slots[i].SetItem(inventory.items[i]);
}
else
{
slots[i].ClearSlot();
}
}
}

슬롯 단위는 ItemSlot에서 아이콘/수량을 표시하고, 더블클릭을 감지해 inventory.UseItem()을 호출하도록 만들었습니다.

ItemSlot.cs (더블클릭 → 사용)

public void OnPointerClick(PointerEventData eventData)
{
    if(Time.time - lastClickTime < doubleClickDelay)
    {
        OnDoubleClick();
    }
    lastClickTime = Time.time;
}

public void OnDoubleClick()
{
if(currentItem != null)
{
inventory.UseItem(currentItem);
}
}

 

 

🧪  Use/Equip 더블클릭 액션 뼈대

사용 아이템은 허기/갈증 회복 같은 효과가 들어갈 예정이라, 우선은 수량 감소 → 0이면 삭제만 동작하도록 뼈대를 만들어 두었습니다. 장착(Equip)은 다음 단계에서 장비창과 연결하면서 붙일 예정입니다.

Inventory.UseItem() (WIP)

public void UseItem(Item item)
{
    if (item.data.DoubleClickAction == DoubleClickAction.Use)
    {
        if (item.data.HungerRestoration > 0)
        {
            // 허기 회복 처리 예정
        }
        if (item.data.ThirstRestoration > 0)
        {
            // 갈증 회복 처리 예정
        }

```
    item.currentAmount--;
    if (item.currentAmount <= 0)
    {
        RemoveItem(item);
    }
}
else if (item.data.DoubleClickAction == DoubleClickAction.Equip)
{
    // 장착 처리 예정
}
OnInventoryChanged?.Invoke();
```

}

 

 

🧷 UIManager 수정 (인벤토리 패널 토글)

UIManager는 인벤토리 패널을 켜고/끄는 토글 기능을 추가했습니다. 지금은 씬 전환을 고려해서 DontDestroyOnLoad를 붙여두었는데, 나중에 씬 구조가 확정되면 “정말 UIManager가 전역으로 남아야 하는지”는 한 번 더 점검할 예정입니다.

UIManager.cs

[SerializeField] private GameObject inventoryPanel;

private static UIManager Instance;

private void Awake()
{
if(Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}

public void ToggleInventory()
{
inventoryPanel.SetActive(!inventoryPanel.activeSelf);
}

 

 

 

📌 오늘의 회고

 

오늘은 인벤토리 시스템을 “일단 눈에 보이게” 만들기 위한 첫 삽을 뜬 날이었습니다.

특히 데이터(SO) → 런타임(Item) → 인벤토리(목록) → UI(슬롯) 흐름이 한 번에 연결되니까, 앞으로 장비창/퀵슬롯/드롭 아이템 창까지 확장할 때도 기준점이 생긴 느낌이었습니다.

다만 지금 구조는 아직 Player와의 연결, 그리고 목표였던 드래그 앤 드롭 기반 아이템 이동이 들어가기 전 단계라서, 다음부터는 난이도가 확 올라갈 것 같습니다. (벌써부터 예외 케이스가 보이는 느낌…)