오늘은 갑자기 게임 인벤토리에서 아이템을 드래그해서 다른 슬롯으로 옮기는 기능을 직접 구현해보고 싶어졌다.
많은 게임들에서 인벤토리 아이템을 마우스로 드래그해서 위치를 바꾸는 기능이 당연하게 들어가 있는데, 내가 직접 만들어 본 적은 없었다.
마침 ScriptableObject로 아이템을 만들어둔 게 있어서, 그걸 이용해서 간단한 드래그 앤 드롭 시스템을 구현해봤다.
아이템 하나하나를 ScriptableObject로 만들어 관리하도록 했다.
이렇게 하면 아이템 데이터를 에셋으로 따로 관리할 수 있어서 인스펙터에서 편하게 세팅할 수 있다.
[CreateAssetMenu(fileName = "Item", menuName = "ScriptableObject/ItemData")]
public class ItemData : ScriptableObject
{
public enum ItemType { Melee, Range }
[Header("Main Info")]
public ItemType Type;
public int itemId;
public string itemName;
public string itemDescription;
public Sprite itemIcon;
}
각 인벤토리 슬롯은 아이템 데이터를 보관하고 있어야 한다.
드래그를 시작하면 아이콘이 마우스를 따라다닌다.
드롭하면 두 슬롯의 아이템을 서로 교환한다.
UI 상의 한 슬롯을 담당하는 스크립트를 작성했다. 이 스크립트는 드래그 이벤트를 처리하고 아이템 데이터를 바꿔주는 역할을 한다.
▸ 인터페이스 구현
유니티의 UI 이벤트를 감지하기 위해 다음 인터페이스들을 구현한다:
IBeginDragHandler, IDragHandler, IEndDragHandler: 드래그 관련 이벤트
IDropHandler: 드롭 이벤트 처리
// 유니티 UI 시스템과 이벤트 시스템 관련 네임스페이스
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
// 인벤토리 슬롯 하나를 담당하는 클래스
// 드래그 앤 드롭 이벤트를 처리하기 위해 여러 인터페이스를 구현
public class InventorySlot : MonoBehaviour,
IBeginDragHandler, IDragHandler, IEndDragHandler, IDropHandler
{
// 슬롯에 표시될 아이템 아이콘 이미지
public Image itemIcon;
// 현재 슬롯에 저장된 아이템 데이터 (ScriptableObject)
public ItemData itemData;
// 드래그한 아이콘을 표시하기 위해 사용하는 Canvas 참조
private Canvas canvas;
// 드래그 중 생성되는 아이콘 오브젝트
private GameObject draggingIcon;
// 초기화: 상위에서 Canvas 컴포넌트를 찾아 저장하고, 슬롯 UI를 업데이트
void Start() {
canvas = GetComponentInParent<Canvas>();
UpdateSlotUI();
}
// 아이템 데이터를 슬롯에 할당하고 UI도 갱신
public void SetItem(ItemData newItem) {
itemData = newItem;
UpdateSlotUI();
}
// 슬롯 UI를 아이템 데이터에 맞춰 갱신
private void UpdateSlotUI() {
// 아이템이 있으면 아이콘 표시, 없으면 비활성화
itemIcon.sprite = itemData ? itemData.itemIcon : null;
itemIcon.enabled = itemData != null;
}
// 드래그 시작 시 호출: 아이템 아이콘이 따라다니도록 새 오브젝트 생성
public void OnBeginDrag(PointerEventData eventData) {
// 아이템이 없으면 드래그 안 함
if (itemData == null) return;
// 드래그 중 보일 아이콘 오브젝트 생성
draggingIcon = new GameObject("Dragging Icon");
draggingIcon.transform.SetParent(canvas.transform, false); // Canvas 하위에 생성
// 아이콘 이미지 추가
var img = draggingIcon.AddComponent<Image>();
img.sprite = itemData.itemIcon;
img.raycastTarget = false; // 드래그 중 raycast를 막지 않도록 설정
// 드래그 아이콘 위치를 마우스 위치로 설정
draggingIcon.transform.position = eventData.position;
}
// 드래그 중 호출: 드래그 아이콘이 마우스를 따라다니게 함
public void OnDrag(PointerEventData eventData) {
if (draggingIcon != null)
draggingIcon.transform.position = eventData.position;
}
// 드래그 끝났을 때 호출: 드래그 아이콘 삭제
public void OnEndDrag(PointerEventData eventData) {
if (draggingIcon != null)
Destroy(draggingIcon);
}
// 다른 슬롯 위에 드래그해서 드롭했을 때 호출
public void OnDrop(PointerEventData eventData) {
// 드래그된 오브젝트가 슬롯인지 확인
var draggedSlot = eventData.pointerDrag?.GetComponent<InventorySlot>();
// 다른 슬롯이면 아이템 교환
if (draggedSlot != null && draggedSlot != this) {
SwapItems(draggedSlot);
}
}
// 두 슬롯의 아이템 데이터를 교환하는 함수
private void SwapItems(InventorySlot otherSlot) {
// 현재 슬롯의 아이템을 임시 저장
var tempData = itemData;
// 다른 슬롯의 아이템을 현재 슬롯에 설정
SetItem(otherSlot.itemData);
// 임시 저장한 아이템을 다른 슬롯에 설정
otherSlot.SetItem(tempData);
}
}
드래그 중 아이콘을 따로 생성해서 마우스를 따라다니게 하는 방식이 깔끔했다.
itemData == null 체크를 안 하면 빈 슬롯 드래그 시 오류가 생긴다.
드래그 중에는 raycastTarget = false로 설정해서 드롭 이벤트가 막히지 않도록 해야 했다.
아이템 간 교환은 SetItem()으로 간단하게 처리할 수 있었다.
아이템 설명 툴팁 (hover 시 표시)
우클릭해서 아이템 사용하기
아이템 버리기 기능
장비 슬롯과 일반 슬롯 구분
사실 막상 해보니 생각보다 어렵진 않았고, 유니티 UI 이벤트 시스템만 잘 이해하면 쉽게 구현할 수 있었다.
다음엔 이걸 기반으로 장비 시스템이나, 인벤토리 저장/로드까지 확장해보고 싶다.
오늘은 여기까지!
