유니티/인벤토리

[Unity] 인벤토리 - 3. 드래그 드롭

이름?없음 2024. 2. 8. 22:48
반응형

이번 글에서는 마지막으로 인벤토리의 드래그 드롭 기능을 구현해 보도록 하겠습니다

반응형

일단 저번에 작성했던 스크립트 일부를 수정했습니다

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Inventory : MonoBehaviour
{
    
    private Dictionary<ItemDataSO, int> itemContainer = new();
    public static Inventory Instance { get; private set; }

    public event Action<ItemDataSO, int> OnItemChanged;
    public event Action<ItemDataSO> OnNewItemAdded;

    private void Awake()
    {
        
        Instance = this;

    }

    public void AddItem(Item item)
    {

        if (itemContainer.ContainsKey(item.itemData))
        {

            if(item.itemData.MaxStackCount > itemContainer[item.itemData])
            {

                itemContainer[item.itemData]++;

                OnItemChanged?.Invoke(item.itemData, itemContainer[item.itemData]);

                Destroy(item.gameObject);

            }

        }
        else
        {

            itemContainer.Add(item.itemData, 1);

            OnNewItemAdded?.Invoke(item.itemData);

            Destroy(item.gameObject);

        }

    }

    public void RemoveItem(ItemDataSO item, Vector2 pos)
    {
        
        if(itemContainer.ContainsKey(item))
        {

            Instantiate(item.ItemPrefab, pos, Quaternion.identity);

            itemContainer[item]--;

            //이벤트 실행
            OnItemChanged?.Invoke(item, itemContainer[item]);

            if (itemContainer[item] <= 0)
            {

                itemContainer.Remove(item);

            }


        }

    }

    public int GetItemCount(ItemDataSO data)
    {

        if (itemContainer.TryGetValue(data, out var count))
        {

            //있다면 카운트를 반환
            return count;

        }

        //아이템이 없다면 -1반환
        return -1;

    }

    private void OnDestroy()
    {
        
        Instance = null;

    }

}
using System;
using UnityEngine;
using UnityEngine.UI;

public class InventoryViewer : MonoBehaviour
{

    [SerializeField] private Slot slotPrefab;
    [SerializeField] private Transform slotParent;
    [SerializeField] private Image changeSlotUI;

    private bool isChanging;
    private Slot currentChangeSlot;

    //현제 포인터가 가리키고있는 슬롯
    private Slot pointerUpSlot;

    private void Start()
    {

        //이벤트 구독
        Inventory.Instance.OnNewItemAdded += HandleItemAdded;

    }

    private void HandleItemAdded(ItemDataSO obj)
    {

        Instantiate(slotPrefab, slotParent).Init(obj);

    }

    //Slot설정
    public void SetPointerSlot(Slot slot)
    {

        pointerUpSlot = slot;

    }

    private void Update()
    {

        if (Input.GetMouseButtonUp(1) && isChanging)
        {

            if(pointerUpSlot != null)
            {

                SwapSlot(pointerUpSlot);

            }

            changeSlotUI.gameObject.SetActive(false);
            currentChangeSlot = null;
            isChanging = false;

        }

        if (isChanging)
        {

            changeSlotUI.transform.position = Input.mousePosition;

        }

    }

    public void StartChange(Slot slot)
    {

        isChanging = true;
        changeSlotUI.gameObject.SetActive(true);
        changeSlotUI.sprite = slot.currentSaveItem.ItemSprite;
        currentChangeSlot = slot;

    }

    public void SwapSlot(Slot slot)
    {
        //같은 슬롯끼리 바꾸려고 한다면 못하도록
        if (slot == currentChangeSlot) return;

        //두 아이템을 바꿔주기
        var oldData = slot.currentSaveItem;

        slot.Init(currentChangeSlot.currentSaveItem);
        currentChangeSlot.Init(oldData);

        currentChangeSlot = null;

    }

}
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;


public class Slot : MonoBehaviour, IPointerDownHandler, IPointerEnterHandler, IPointerExitHandler
{

    private TMP_Text itemCountText;
    private Image itemImage;
    private InventoryViewer inventoryViewer;
    public ItemDataSO currentSaveItem { get; private set; }

    private void Awake()
    {
        
        itemCountText = GetComponentInChildren<TMP_Text>();
        itemImage = transform.Find("ItemImage").GetComponent<Image>();
        inventoryViewer = FindObjectOfType<InventoryViewer>();

    }

    public void Init(ItemDataSO item)
    {

        currentSaveItem = item;
        itemCountText.text = Inventory.Instance.GetItemCount(item).ToString();
        itemImage.sprite = item.ItemSprite;
        
        Inventory.Instance.OnItemChanged += HandleItemAdded;

    }

    private void HandleItemAdded(ItemDataSO item, int count)
    {

        if(currentSaveItem == item)
        {

            if(count == 0)
            {

                Destroy(gameObject);

            }
            else
            {

                itemCountText.text = count.ToString();

            }


        }

    }

    private void OnDestroy()
    {
        
        if(Inventory.Instance != null)
        {

            Inventory.Instance.OnItemChanged -= HandleItemAdded;

        }


    }

    public void OnPointerDown(PointerEventData eventData)
    {

        if(eventData.button == PointerEventData.InputButton.Left)
        {

            Inventory.Instance.RemoveItem(currentSaveItem, Random.insideUnitCircle);

        }
        else if(eventData.button == PointerEventData.InputButton.Right)
        {

            inventoryViewer.StartChange(this);

        }

    }

    //포인터가 가리키는 슬롯 설정
    public void OnPointerEnter(PointerEventData eventData)
    {

        inventoryViewer.SetPointerSlot(this);

    }

    public void OnPointerExit(PointerEventData eventData)
    {

        inventoryViewer.SetPointerSlot(null);

    }

}

 

새로운 스크립트를 작성할 필요도 없이 구현됩니다

그다음으로는 UI 구현입니다

빈 이미지를 하나 추가해 주고 RayCastTarget을 꺼주고 비활성 상태로 만들어 주세요

그다음으로 만든 이미지를 InventoryViewer에 할당해 주세요

그럼 끝났습니다! 잘 작동하는 것을 확인할 수 있습니다

저는 슬롯끼리 바뀌는 것까지만 구현했습니다

 

이제 이 코드를 응용하여 더 다양한 기능이 있는 인벤토리를 구현할 수 있을 것입니다

인벤토리 코드는 패키지로 만들어 첨부해 놓았습니다

Inventory.unitypackage
0.10MB

 

인벤토리 관련 글은 여기까지 하도록 하겠습니다

반응형