오늘 배운 내용
- 팀 프로젝트 아이템, 인벤토리 구현
팀 프로젝트 아이템, 인벤토리 구현
오늘은 인벤토리 기능을 구현했다. 인벤토리의 레퍼런스는 이터널 리턴의 인벤토리로 잡았다
아이템을 10개까지 넣을 수 있게하고 아이템을 주우면 인벤토리 UI에 아이템의 아이콘이 표시된다.
그리고 아이콘을 드래그 하여 인벤토리 밖으로 보내면 아이템이 다시 월드 상에 생성되도록 만들어 보았다.
일단 아직 플레이어가 움직이는 부분을 다른 팀원분이 작업하고 있어서 아이템과 인벤토리를 일단 만들고
레이캐스트를 사용해서 테스트 해 보았다.
public class UIInventory : MonoBehaviour
{
public UIItemSlot[] inventorySlot;
public Dictionary<string, int> itemCount = new Dictionary<string, int>();// 어떤아이템이 몇개있는지
public UICombination UICombination;
Item[] itemInSlot;// 아이템이 슬롯의 어느위치에 있는지
List<int> EmptySlot;
private void Awake()
{
InitInventorySlot();
}
public void InitInventorySlot()
{
inventorySlot = new UIItemSlot[10];
itemInSlot = new Item[10];
EmptySlot = new List<int>();
Transform slotLayout = transform.GetChild(0).GetChild(0);
for (int i = 0; i < 10; i++)
{
inventorySlot[i] = slotLayout.GetChild(i).GetComponent<UIItemSlot>();
EmptySlot.Add(i);
}
}
public void AddToDictinary(string name)
{
if (itemCount.ContainsKey(name))
{
itemCount[name]++;
}
else
{
itemCount.Add(name, 1);
}
}
public void RemoveFromDictionary(Item item)
{
itemCount[item.itemSO.itemName]--;
}
public bool AddItemToInventory(Item item)
{
if (EmptySlot.Count <= 0) return false;
EmptySlot.Sort();
int index = EmptySlot[0];
EmptySlot.RemoveAt(0);
AddToDictinary(item.itemSO.itemName);
itemInSlot[index] = item;
inventorySlot[index].AddItemUI(item.itemSO);
UICombination.UpdateUICombination();
item.transform.SetParent(GameManager.Instance.transform);
return true;
}
public void RemoveItemFromInventory(int index)
{
if (itemInSlot[index] == null) return;
Item item = itemInSlot[index];
itemInSlot[index] = null;
EmptySlot.Add(index);
inventorySlot[index].RemoveItemUI();
item.gameObject.SetActive(true);
// 아이템의 위치를 플레이어의 위치로 변경해줘야됨 (드랍)
RemoveFromDictionary(item);
UICombination.UpdateUICombination();
}
}
구현한 인벤토리에서 중요한 점은 빈 공간을 List로 관리하며
아이템을 주울때 빈 공간 List를 정렬하여 앞에서부터 인벤토리가 채워지도록 해주었다.
또 Item배열을 이용해 현재 아이템이 어느슬롯에 위치하고 있는지를 저장해두었고 Dictionary에 아이템을 종류별로 따로 저장해 두었다.
사실 처음에는 이렇게 자료구조를 여러개를 써야한다고 생각을 안했는데 나중에 기능을 추가하다 보니
계속 필요한 부분이 생겨서 추가가 되었다. 일단 기능 구현이 끝나고나서 꼭 필요한 데이터만 저장하고 있도록
개선점을 생각해 봐야한다.
using System;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
[Serializable]
public class UIItemSlot : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IDragHandler
{
private Vector2 initialPosition;
private Vector2 lastDragPosition;
private RectTransform limitZone;
private UIInventory inventory;
public Image icon;
private void Awake()
{
inventory = GetComponentInParent<UIInventory>();
limitZone = transform.parent.GetComponent<RectTransform>();
}
public void OnDrag(PointerEventData eventData)
{
Vector2 delta = eventData.position - lastDragPosition;
transform.position = (Vector2)transform.position + delta;
lastDragPosition = eventData.position;
}
public void OnPointerDown(PointerEventData eventData)
{
initialPosition = transform.position;
lastDragPosition = eventData.position;
}
public void OnPointerUp(PointerEventData eventData)
{
if (!RectTransformUtility.RectangleContainsScreenPoint(limitZone, eventData.position))
{
//아이템을 인벤토리에서 꺼내서 바닥에버림 나중에 플레이어의 위치로 변경
inventory.RemoveItemFromInventory(transform.GetSiblingIndex());
}
transform.position = initialPosition;
}
public void AddItemUI(ItemSO itemSO)
{
icon.sprite = itemSO.icon;
icon.gameObject.SetActive(true);
}
public void RemoveItemUI()
{
icon.sprite = null;
icon.gameObject.SetActive(false);
}
}
슬롯을 드래그 하는 부분은 구글링을 통해 찾을 수 있었는데,
드래그 구현을 위한 인터페이스들을 유니티에서 지원해주고 있었고 해당 인터페이스를 구현해서 기능을 구현했다.
또 Icon이 바뀌는 부분을 처음에는 Inventory쪽에서 했었는데 Slot쪽에서 처리를 하는게 맞다고 생각해서
ItemSO 정보를 받아서 수정하도록 구현했다.
아이템에 관한 정보는 ScirptableObject로 관리했다.
아이템은 상호작용 가능한 물체들과 상호작용이 끝나면 해당 위치에 드롭되고
현재는 마우스로 클릭하면 레이캐스트를 통해 메서드를 실행하고 아이템을 인벤토리에 넣는다
테스트를 해보다가 알게된 문제점은 아이템의 게임 오브젝트 자체를 인벤토리에 넣은게 아니라서
맵 이동을 해서 이전 맵이 사라지게 되면 연결된 게임 오브젝트가 사라져 아이템을 인벤토리에서 버리는 부분이
제대로 작동하지 않았다 일단은 GameManager은 DontDestroyOnLoad로 만들어 줄텐데 다른 부분은 예정이 없어서
아이템을 인벤토리에 넣을때는 GameManager로 옮겨지도록 임시로 기능을 구현해 두었다.
using System;
using UnityEngine;
[CreateAssetMenu(fileName = "ItemData", menuName = "ItemData/Default", order = 0)]
[Serializable]
public class ItemSO : ScriptableObject
{
[Header("ItemInfo")]
public string itemName;
public string itemInfo;
public Sprite icon;
}