🧠 오늘의 핵심 정리
- 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 쪽은 UIInventory가 Inventory.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와의 연결, 그리고 목표였던 드래그 앤 드롭 기반 아이템 이동이 들어가기 전 단계라서, 다음부터는 난이도가 확 올라갈 것 같습니다. (벌써부터 예외 케이스가 보이는 느낌…)
'내일배움캠프 본캠프' 카테고리의 다른 글
| [내일배움캠프 46일차 TIL] 프로토타입 4일차 '대사창' (0) | 2025.12.03 |
|---|---|
| [내일배움캠프 45일차 TIL] 프로토타입 3일차 'Json 데이터로 아이템 제작' (0) | 2025.12.02 |
| [내일배움캠프 43일차 TIL] 프로토타입 1일차 '팀 편성' (0) | 2025.11.28 |
| [내일배움캠프 42일차 TIL] 인벤토리 완성 (0) | 2025.11.27 |
| [내일배움캠프 41일차 TIL] UI인벤토리 (0) | 2025.11.26 |