게임의 다양한 아이템들을 관리하기 위해 대부분의 RPG게임에는 인벤토리가 있다. 플레이하면서 얻은 아이템들은 인벤토리에 수납하고, 필요시 꺼내서 사용할 수 있게하는 장치이다. 게임에 따라 유형도 굉장히 다양한데 보통 특정한 숫자의 인벤토리 공간(슬롯)이 있고, 그 빈 슬롯에 새로 얻은 아이템이 채워지는 형태를 많이 사용한다. 디아블로가 그러함.
특정한 크기의 공간이 있고, 그걸 채운다..? 어? 이거 완전 배열인데? 바로 만들어봤다.
뭐가 필요할지 생각해봤다. 일단 인벤토리를 배열로 만든다치면, 그 안을 채울 내용물인 아이템이 필요할 것이고, 그 아이템을 담아둘 각각의 슬롯이 필요하겠다. 기본적인 기능은 이게 전부고, 그 외에 기능은 추가적으로 만들면 될 듯. 일단 인벤토리 내에서 아이템을 식별할 수 있는 스프라이트 이미지가 필요할테니 스프라이트들을 리소스폴더에 넣어놨다.
그리고 간단한 아이템과 16칸짜리 인벤토리 구조를 만들었다.
using UnityEngine;
public class Item
{
public string name;
public string description;
}
public class ItemSlot
{
public Item slotItem;
public bool isEmpty = true;
}
public class Inventory
{
public ItemSlot[] inventory = new ItemSlot[16];
}
한개의 슬롯은 하나의 아이템을 담당하고, 그 슬롯들을 인벤토리에서 관리함. 근데 이럼 사실상 끝 아님? 물론 아님.
코드로 구성되어있는 배열 인벤토리는 사용자와 상호작용하기 어렵다. GUI를 통해 직관적으로 확인할 수 있어야함. 또한 게임의 인벤토리에는 배열의 기능을 포함한 다양한 기능들이 있따. 일단 각 개별 슬롯엔 아이템을 자유롭게 수납이 가능해야 하겠고, 내가 원하는 슬롯에서 다른 슬롯으로의 이동도 자유로우면 편리할 것 같다. 일단 마우스 드래그로 칸을 아이템을 이동시키는것부터 만들어봐야겠음.
인벤토리 슬롯 자식으로 사용할 이미지를 관리하기 위해 InventoryItem이라는 클래스를 새로 만들었다. 여기에 현재 슬롯에 있는 아이템의 정보를 보관하고, 유니티 DragHandler를 이용해 부모를 현재 마우스커서가 위치하는 슬롯으로 변경시키는 코드를 작성했음.
using System;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class InventorySlot : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
public Item slotItem;
public bool isEmpty = true;
public Action<InventorySlot> onPointerEnter;
public Action<InventorySlot> onPointerExit;
private Image slotImage;
private void Awake()
{
slotImage = GetComponent<Image>();
}
public void OnPointerEnter(PointerEventData eventData)
{
onPointerEnter?.Invoke(this);
slotImage.color = Color.yellow;
}
public void OnPointerExit(PointerEventData eventData)
{
onPointerExit?.Invoke(this);
slotImage.color = Color.white;
}
public void RefreshItemSlot()
{
}
}
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class InventoryItem : MonoBehaviour, IBeginDragHandler, IEndDragHandler, IDragHandler
{
private Item slotItem;
private Image image;
private Sprite itemSprite;
[SerializeField] private bool isDragging = false;
[SerializeField] private Inventory inventory;
[SerializeField] private InventorySlot currentSlot;
private void Awake()
{
image = GetComponent<Image>();
inventory = GetComponentInParent<Inventory>();
currentSlot = GetComponentInParent<InventorySlot>();
}
public void OnBeginDrag(PointerEventData eventData)
{
isDragging = true;
image.raycastTarget = false;
transform.SetParent(inventory.hoveringItemTransform);
}
public void OnDrag(PointerEventData eventData)
{
if (isDragging)
{
transform.position = eventData.position;
}
}
public void OnEndDrag(PointerEventData eventData)
{
isDragging = false;
image.raycastTarget = true;
if (inventory.currentHoveringSlot != null)
{
currentSlot = inventory.currentHoveringSlot.GetComponent<InventorySlot>();
}
transform.SetParent(currentSlot.transform);
transform.localPosition = Vector3.zero;
}
}
근데 이러면 이미 아이템이 있는 칸에 드롭하면 어떻게 하나요? 아이템이 겹쳐서 놓이게 된다. 근데 이럼 안되잖아? 그러면 일단 슬롯이 비어있는지 확인 후에 아이템을 옮겨줘야한다.제발 내게 이러지 말아줘
private void SwapItem(InventorySlot prevSlot, InventorySlot nextSlot)
{
InventoryItem otherItem = nextSlot.transform.GetComponentInChildren<InventoryItem>();
otherItem.transform.SetParent(prevSlot.transform);
otherItem.transform.localPosition = Vector3.zero;
otherItem.currentSlot = prevSlot;
}
슬롯이 비어있는지 먼저 확인한 후 요소가 있다면 스왑해주면됨.
아이템 외형만 가지고 어떤 아이템인지 물론 알 수도 있다. 근데 게임이 커지고 아이템 종류가 많아지면 하나하나 기억하고 있긴 힘들 노릇. 그래서 마우스를 올리면 아이템의 세부 정보를 알려주는 UI를 만들어 어떤 아이템인지 확인 할 수 있게 만드는 기능을 만들어봤다. 일단 지금 아이템엔 이름과 설명밖에 없으니 간단하게 외형을 만들었다. 나중에 아이템의 종류나 기능이 늘어나면 유니티 UI의 LayoutGroup
과 ContentSizefitter
를 활용하면 될 듯.
아이템UI는 전에 만들어놓은 UIManager
를 통해 호출해줬다. 아이템UI는 앵커를 좌하단으로 잡고 Update
에서 마우스 위치를 추적하게 만들면 마우스 위쪽에서 따라다니게됨. 일단 아이템 동일한 아이템 정보를 넣어놓고 테스트 해봤다.하이라키창에선 이런식으로 동작함.
이제 개별적인 슬롯과 아이템의 기능은 얼추 만들었으니 이제 이걸 관리할 Inventory를 만들어야 된다. 제발 누가 대신 해줘~~