🛠️ 오늘 한 작업
- 융합을 앞두고 지금까지 만든 인벤토리 / 장비 / 퀵슬롯 코드를 다시 정리했습니다.
- 사용 버튼과 장착 관련 메서드를 빨리 정리하는 것을 목표로 잡았습니다.
- 퀵슬롯 수정 중 작업을 진행했습니다.
- 퀵슬롯에 등록된 아이템을 선택했을 때 테두리가 빨간색으로 표시되도록 UI를 수정했습니다.
- 퀵슬롯 등록 방식을
ItemStack직접 참조에서QuickBinding구조로 바꾸기 시작했습니다. - 플레이어 인벤토리 연결 작업으로 인벤토리 / 장비 / 퀵슬롯을 실제 플레이어 쪽에 연결했습니다.
📌 오늘 계획
오늘은 융합을 위해 지금까지 만든 기능을 조금 더 실제로 연결 가능한 상태로 만드는 것이 목표였습니다.
인벤토리, 장비창, 퀵슬롯을 각각 만들고는 있었지만, 이제는 따로따로 돌아가는 기능이 아니라 실제 플레이어와 연결되어야 했습니다.
- 융합을 위해 사용 버튼과 장착 관련 메서드 정리하기
- 지금까지 만든 코드 다시 정리해서 보기
- 퀵슬롯 장착 시 선택 테두리 넣기
- 플레이어 인벤토리와 장비, 퀵슬롯 연결하기
특히 퀵슬롯 쪽은 아직 수정 중인 상태였습니다. 장착까지는 어느 정도 됐지만, 번호를 누르지 않으면 장착이 안 되는 문제와 퀵슬롯 간 스왑이 어색한 문제가 남아 있었습니다.
⚡ 퀵슬롯 바인딩 방식 수정
오늘은 퀵슬롯 구조를 다시 손봤습니다.
기존에는 퀵슬롯이 인벤토리의 ItemStack을 직접 참조하는 방식이었습니다. 그런데 이 방식은 아이템이 장착되거나 인벤토리에서 위치가 바뀌면 참조를 어떻게 유지할지 계속 신경 써야 했습니다.
그래서 QuickBinding이라는 구조를 만들어서, 퀵슬롯이 아이템을 어떤 기준으로 바라볼지 나눴습니다.
- 무기처럼 개별 상태가 중요한 아이템은
uid기준 - 소비 아이템처럼 같은 아이템 ID를 묶어서 봐도 되는 경우는
itemId기준
QuickSlot.cs - QuickBinding 구조
public struct QuickBinding
{
public QuickBindType type;
public int itemId;
public long uid;
public static QuickBinding FromUid(long uid, int itemId)
{
return new QuickBinding
{
type = QuickBindType.Uid,
uid = uid,
itemId = itemId
};
}
public static QuickBinding FromItemId(int itemId)
{
return new QuickBinding
{
type = QuickBindType.ItemId,
uid = 0,
itemId = itemId
};
}
}
이 구조가 필요했던 이유는 무기와 소비 아이템의 성격이 다르기 때문입니다.
무기는 내구도 같은 개별 상태가 있기 때문에 단순히 itemId만 보고 처리하면 안 됩니다. 반면 소비 아이템은 같은 아이템 ID를 기준으로 수량을 줄이는 방식이 더 자연스럽습니다.
그래서 퀵슬롯에서도 무기와 소비 아이템을 같은 방식으로 묶지 않고, 서로 다른 기준으로 처리하도록 바꾸기 시작했습니다.
🧷 아이템 UID 추가
퀵슬롯에서 무기를 uid 기준으로 찾기 위해, ItemStack에도 고유 ID를 추가했습니다.
기존에는 아이템을 itemId와 count 중심으로 보고 있었는데, 내구도가 있는 아이템은 같은 itemId여도 완전히 같은 아이템이라고 보기 어렵습니다.
ItemStack.cs - uid 추가
[System.Serializable]
public class ItemStack
{
public long uid;
public int itemId;
public int count;
public int maxStack;
public ItemRuntime runtime;
public bool HasRuntime => runtime != null;
public ItemStack(long uid, int itemId, int count, int maxStack, ItemRuntime runtime = null)
{
this.uid = uid;
this.itemId = itemId;
this.count = count;
this.maxStack = Mathf.Max(1, maxStack);
this.runtime = runtime;
}
}
인벤토리에서는 새 아이템을 만들 때마다 uid를 부여하도록 했습니다. 그리고 퀵슬롯이나 장비창에서 다시 해당 아이템을 찾아야 할 때 FindIndexByUid로 인벤토리 안의 위치를 찾을 수 있게 했습니다.
이 부분은 나중에 저장 기능까지 생각하면 더 중요해질 것 같습니다. 지금은 당장 퀵슬롯과 장비 연결 때문에 넣은 값이지만, 결국 아이템 개별 상태를 유지하려면 이런 식별자가 필요하다고 느꼈습니다.
🔁 퀵슬롯 이동 / 스왑 수정
퀵슬롯 이동도 다시 수정했습니다.
처음에는 단순히 한 슬롯에서 다른 슬롯으로 옮기는 방식에 가까웠는데, 실제로 사용해보면 이미 다른 퀵슬롯에 아이템이 있을 때 서로 바뀌는 흐름도 필요했습니다.
QuickSlot.cs - 퀵슬롯 이동 / 교환
public bool Move(QuickSlotType from, QuickSlotType to)
{
if (from == to) return false;
if (!quickSlots.TryGetValue(from, out var fromBinding)) return false;
if (quickSlots.TryGetValue(to, out var toBinding))
{
quickSlots[to] = fromBinding;
quickSlots[from] = toBinding;
}
else
{
quickSlots.Remove(from);
quickSlots[to] = fromBinding;
}
EventManager.TriggerEvent(EventKey.QuickSlotChanged);
return true;
}
이 수정으로 퀵슬롯끼리 드래그했을 때, 대상 슬롯이 비어 있으면 이동하고 이미 아이템이 있으면 서로 바뀌는 방향으로 처리할 수 있게 했습니다.
아직 완전히 깔끔한지는 더 봐야 하지만, 기존보다 퀵슬롯의 동작 기준이 조금 더 명확해졌습니다.
🔴 선택된 퀵슬롯 테두리 표시
퀵슬롯에서 현재 선택된 슬롯을 알 수 있도록 테두리 표시도 추가했습니다.
전투 중에는 어떤 퀵슬롯이 선택되어 있는지 바로 보여야 하기 때문에, 선택 슬롯에 하이라이트 프레임을 넣는 방식으로 작업했습니다.
UIQuickSlot.cs - 선택 슬롯 표시
[SerializeField] private Image[] highlightFrames;
[SerializeField] private Sprite selectedFrameSprite;
[SerializeField] private Sprite normalFrameSprite;
public void SetSelected(int index)
{
for (int i = 0; i < highlightFrames.Length; i++)
{
if (highlightFrames[i] == null) continue;
bool selected = i == index;
if (selectedFrameSprite != null)
{
highlightFrames[i].sprite = selected
? selectedFrameSprite
: normalFrameSprite;
}
highlightFrames[i].enabled = selected || normalFrameSprite != null;
}
}
이전에는 퀵슬롯에 아이템이 들어가는지만 확인했다면, 이제는 어느 슬롯이 선택되어 있는지도 UI에서 보여주려는 단계로 넘어갔습니다.
작은 UI처럼 보이지만, 실제 플레이에서는 이런 표시가 없으면 현재 어떤 무기나 아이템을 선택한 건지 헷갈릴 수 있습니다.
🎮 번호 입력으로 퀵슬롯 선택
퀵슬롯은 번호 입력과도 연결했습니다.
QuickSlotController에서 현재 선택된 퀵슬롯 인덱스를 관리하고, 번호 입력이 들어오면 해당 슬롯을 선택하도록 만들었습니다.
QuickSlotController.cs - 번호 입력 선택
public void SelectSlotByNumber(int number)
{
int index = Mathf.Clamp(number - 1, 0, 4);
if (index == SelectedIndex) return;
SelectedIndex = index;
ApplySelection();
}
public void OnQuick1() => SelectSlotByNumber(1);
public void OnQuick2() => SelectSlotByNumber(2);
public void OnQuick3() => SelectSlotByNumber(3);
public void OnQuick4() => SelectSlotByNumber(4);
public void OnQuick5() => SelectSlotByNumber(5);
아직은 번호를 눌렀을 때 무기 아이템이 장착되는 느낌인데 나중에 소비 아이템이 소비되는 메서드도 추가할 예정입니다.
🧍 플레이어 인벤토리와 연결
오늘 가장 큰 구조 변경 중 하나는 인벤토리 / 장비 / 퀵슬롯을 실제 플레이어에 연결한 부분입니다.
이전에는 GameManager가 플레이어 1, 플레이어 2의 인벤토리와 장비를 직접 들고 있는 방식이었습니다. 그런데 이 방식은 플레이어 데이터가 늘어나거나 실제 플레이어 오브젝트와 연결할 때 애매해질 수 있었습니다.
그래서 Player가 자신의 inventory, equipment, quickSlot을 직접 가지고, GameManager는 현재 활성 플레이어를 통해 접근하는 방식으로 바꾸었습니다.
Player.cs - 플레이어가 인벤토리 / 장비 / 퀵슬롯 소유
public Inventory inventory;
public Equipment equipment;
public QuickSlot quickSlot;
private void Awake()
{
inventory = new Inventory();
equipment = new Equipment();
quickSlot = new QuickSlot();
}
private void Start()
{
GameManager.Instance.ActivePlayer = this;
}
GameManager.cs - 활성 플레이어 기준 접근
public Player ActivePlayer;
public Inventory GetActiveInventory()
{
return ActivePlayer.inventory;
}
public Equipment GetActiveEquipment()
{
return ActivePlayer.equipment;
}
이 구조가 더 자연스럽다고 느꼈습니다. 인벤토리와 장비는 결국 플레이어가 가지고 있는 데이터이기 때문에, GameManager가 전부 직접 들고 있는 것보다 플레이어 안에 두는 쪽이 더 맞아 보였습니다.
다만 이 구조로 바꾸면 UI 쪽에서도 항상 현재 활성 플레이어를 기준으로 데이터를 가져와야 합니다. 그래서 ItemTransferService, UIQuickSlot, QuickSlotCancel 같은 곳도 GameManager.Instance.ActivePlayer를 기준으로 접근하도록 수정했습니다.
🧹 아이템 생성 흐름도 정리
인벤토리의 아이템 추가 흐름도 같이 정리했습니다.
기존에는 인벤토리에서 직접 내구도 아이템인지 판단하는 코드가 있었는데, 이제는 ItemDB 쪽에 IsStackable, GetMaxStack, NeedsRuntime 같은 판단 메서드를 추가해서 아이템 데이터 기준으로 처리하도록 바꿨습니다.
ItemDB.cs - 아이템 판단 메서드
public bool IsStackable(int itemId)
{
var baseData = GetBase(itemId);
if (baseData == null) return false;
return baseData.MaxStack > 1;
}
public bool NeedsRuntime(int itemId)
{
var type = GetItemType(itemId);
if (type == ItemType.Weapon)
{
if (TryGetWeapon(itemId, out var weapon))
{
return (WeaponCategory)weapon.WeaponCategory != WeaponCategory.Ranged;
}
return false;
}
return type == ItemType.Head
|| type == ItemType.Body
|| type == ItemType.Leg;
}
이건 코드 정리 측면에서도 필요했습니다. 인벤토리가 아이템의 세부 규칙을 너무 많이 알고 있으면 나중에 유지보수가 어려워질 것 같았습니다.
그래서 아이템이 스택 가능한지, 런타임 데이터가 필요한지는 ItemDB가 판단하고, 인벤토리는 그 결과에 따라 아이템을 넣는 역할에 집중하도록 방향을 잡았습니다.
📌 오늘의 회고
오늘은 퀵슬롯을 계속 수정하면서, 결국 인벤토리와 장비, 퀵슬롯이 전부 따로 놀면 안 된다는 걸 다시 느꼈습니다.
처음에는 퀵슬롯에 아이템을 등록하고 아이콘만 보여주면 될 줄 알았는데, 실제로는 선택된 슬롯 표시, 번호 입력, 무기 장착, 소비 아이템 사용, 인벤토리와의 연결까지 전부 생각해야 했습니다.
특히 ItemStack에 uid를 추가하면서, 아이템을 단순히 ID로만 보는 방식으로는 부족하다는 걸 느꼈습니다. 내구도가 있는 무기나 장비는 같은 아이템 ID여도 각각 다른 상태를 가질 수 있기 때문에, 개별 식별이 필요했습니다.
또 오늘은 플레이어 쪽에 인벤토리 / 장비 / 퀵슬롯을 연결했습니다. 이 부분은 구조상 꽤 중요한 변경이었습니다. 앞으로 다른 시스템과 융합하려면 결국 플레이어가 자신의 데이터를 들고 있고, UI는 활성 플레이어를 기준으로 보여주는 구조가 더 맞는 것 같습니다.
아직 퀵슬롯을 완성하진 않았지만 전에 프로토 때, 퀵슬롯을 못 만들었던 걸 생각하면 그때에 비해 많이 성장한 것 같아서 기분이 좋았습니다. (o゜▽゜)o☆
'내일배움캠프 본캠프' 카테고리의 다른 글
| [내일배움캠프 61일차 TIL] 최종 프로젝트 13일차 - 가방 장착 처리와 퀵슬롯 아이템 사용 보완 (0) | 2025.12.24 |
|---|---|
| [내일배움캠프 60일차 TIL] 최종 프로젝트 12일차 - 퀵슬롯 상태 표시와 소비 아이템 사용 기능 (0) | 2025.12.23 |
| [내일배움캠프 58일차 TIL] 최종 프로젝트 10일차 - 실전 모의 면접과 퀵슬롯 구조 수정 (0) | 2025.12.19 |
| [내일배움캠프 57일차 TIL] 최종 프로젝트 9일차 - 모의 면접 준비와 퀵슬롯 구조 시작 (0) | 2025.12.18 |
| [내일배움캠프 56일차 TIL] 최종 프로젝트 8일차 - 드래그 앤 드롭 시도와 아이템 텍스트 표시 정리 (0) | 2025.12.17 |