🧠 오늘의 핵심 정리

  • AddListener로 버튼 이벤트를 코드에서 유연하게 관리
  • ScriptableObject(ItemData)로 아이템 데이터를 분리해서 관리
  • Scroll View + 슬롯 프리팹 기반 인벤토리 UI 구성 + 스크롤 초기화
  • 아이템 상세 패널 구현 : 사용/장착/해제/삭제 버튼 + 상태에 따른 버튼 분기

 

 

🎒 41일차 : UI로 인벤토리 & 스탯창 만들기

게임을 만들다 보면 결국 UI를 만질 일이 생기게 됩니다. (도망 못 감)

그래서 오늘은 “언젠가 해야지…” 하던 인벤토리창 / 스탯창을 UI로 직접 만들어보는 시간을 가졌습니다.

 

 

🧷  AddListener 활용 : 버튼 이벤트를 코드로 정리

버튼 이벤트를 처리할 때 AddListener를 사용하니까 확실히 코드가 깔끔해지고, 버튼 기능을 유연하게 붙일 수 있어서 정말 편했습니다.

특히 오늘은 “아이템 상세 패널”처럼 버튼이 여러 개인 UI가 있었는데, 인스펙터에서 하나씩 연결하는 것보다 코드로 한 번에 정리하는 게 훨씬 관리하기 좋았어요.

UIStatus.cs : 버튼 클릭 시 스탯 텍스트 갱신

void Awake()
{
    openButton.onClick.AddListener(OpenClicked);
    closeButton.onClick.AddListener(CloseClicked);
}

public void SetCharacterStat()
{
attackTXT.text = GameManager.Instance.player.Attack.ToString();
defenseTXT.text = GameManager.Instance.player.Defense.ToString();
healthTXT.text = GameManager.Instance.player.Health.ToString();
criticalTXT.text = GameManager.Instance.player.Critical.ToString();
}

private void OpenClicked()
{
UIManager.Instance.OpenStatus();
SetCharacterStat();
}

메인 메뉴 텍스트(직업/이름/레벨/설명/골드)도 같은 방식으로 버튼 눌렀을 때 갱신되게 처리했습니다.

 

 

📦  ScriptableObject로 아이템 관리

아이템 데이터를 ScriptableObject(ItemData)로 만들었습니다.

데이터 관리가 직관적이고 재사용성도 높아서 “아이템 종류가 많아지면 많아질수록 이게 진짜 빛을 보겠다” 싶었어요.

ItemData.cs : 아이템 타입/스택/효과까지 확장

[CreateAssetMenu(fileName ="NewItem",menuName ="Item")]
public class ItemData : ScriptableObject
{
    public string _name;
    public Sprite icon;
    public int count;
    public string description;

```
public bool canStack;
public int maxStackAmount;

public ItemType type;
public ItemDataConsumable[] consumables;
public ItemDataEquipableType[] equipables;
```

}

그리고 런타임에서는 Item 클래스로 감싸서 현재 개수(CurrentCount), 장착 여부(IsEquipped) 같은 상태를 따로 관리했습니다.

 

 

🧾  Scroll View 첫 사용 : 슬롯 프리팹으로 인벤토리 UI 구성

이번에 처음으로 Scroll View를 사용해봤습니다.

인벤토리 아이템이 많아질 때 스크롤로 깔끔하게 보여줄 수 있어서 앞으로 UI 확장할 때 진짜 유용하겠다고 느꼈어요.

UIInventory.cs : 캐릭터 인벤토리를 기준으로 슬롯 생성

public void InitInventoryUI(Character character)
{
    foreach(var slot in slots)
    {
        Destroy(slot.gameObject);
    }
    slots.Clear();

```
foreach(var item in character.Inventory)
{
    UISlot newSlot = Instantiate(slotPrefab, slotParent);
    newSlot.SetItem(item);
    slots.Add(newSlot);
}
```

}

public void ResetScrollRect()
{
scrollRect.normalizedPosition = new Vector2(0,1);
}

닫을 때 스크롤 위치가 이상하게 남는 게 거슬려서, 닫는 순간 normalizedPosition으로 스크롤을 위로 다시 올리는 것도 같이 처리했습니다.

 

 

🔍  아이템 상세 패널 : 사용/장착/해제/삭제 버튼 분기

그리고 오늘 제일 “게임 UI 같다” 싶었던 부분이 아이템 상세 패널이었습니다.

슬롯을 클릭하면 상세 패널이 열리고, 아이템 타입에 따라 사용/장착/해제 버튼이 달라지게 구현했습니다.

UISlot.cs : 슬롯 버튼 클릭 시 상세 패널 열기

void Awake()
{
    openButton.onClick.AddListener(() =>
        UIManager.Instance.OpenInventoryDetail(currentItem));
}

UIManager.cs : 상세 패널 열기 + 버튼 리스너 동적 연결

public void OpenInventoryDetail(Item item)
{
    _panel.SetActive(true);
    RefreshUIInventoryDetail(item);

```
useButton.onClick.RemoveAllListeners();
equipButton.onClick.RemoveAllListeners();
unEquipButton.onClick.RemoveAllListeners();
deleteButton.onClick.RemoveAllListeners();

useButton.onClick.AddListener(() => {
    GameManager.Instance.player.EatItem(item);
    RefreshUIInventoryDetail(item);
});

equipButton.onClick.AddListener(() => {
    GameManager.Instance.player.EquipItem(item);
    RefreshUIInventoryDetail(item);
});

unEquipButton.onClick.AddListener(() => {
    GameManager.Instance.player.UnEquipItem(item);
    RefreshUIInventoryDetail(item);
});

deleteButton.onClick.AddListener(() => {
    DeleteItem(item);
    CloseInventoryDetail();
});

// 타입에 따른 버튼 분기
if (item.Data.type == ItemType.Equipable)
{
    useButton.gameObject.SetActive(false);
    equipButton.gameObject.SetActive(!item.IsEquipped);
    unEquipButton.gameObject.SetActive(item.IsEquipped);
}
else
{
    useButton.gameObject.SetActive(true);
    equipButton.gameObject.SetActive(false);
    unEquipButton.gameObject.SetActive(false);
}
```

}

그리고 장착 아이템은 슬롯에 개수 대신 E를 표시하게 해서 “장착중”이란 걸 한눈에 볼 수 있게 했습니다.

UISlot.cs : 장착 표시(E)

itemCountTXT.text = currentItem.CurrentCount > 1 ? currentItem.CurrentCount.ToString() : "";
if (currentItem.Data.type == ItemType.Equipable && currentItem.IsEquipped)
{
    itemCountTXT.text = "E";
}

 

 

📌 느낀 점

오늘 해보면서 다시 느낀 건, 이벤트 처리(AddListener), 데이터 관리(ScriptableObject), UI 컴포넌트(Scroll View) 이 세 가지가 진짜 중요하다는 점이었습니다.

특히 인벤토리는 “기능만 되는 것”보다 보기도 편하고, 조작도 자연스럽게 만들어야 진짜 게임 같아지는 것 같아요.

앞으로도 이런 기능들을 적극적으로 활용해서, UI가 늘어나도 관리 가능한 구조로 계속 만들어봐야겠습니다.

 

 

🔜 내일 할 일

  • 아이템 상세 패널 UI/UX 다듬기 (버튼 활성/비활성, 텍스트 표시 예외처리)
  • 스탯창과 인벤토리 연동 정리 (장착/해제 시 스탯 자동 갱신)