스파르타코딩클럽 게임개발

오늘은 스파르타 코딩 클럽 unity 게임 개발 과정 45일차(인벤토리 시스템 2)

코드천자문 2024. 1. 4. 13:54
반응형

유니티 인벤토리 시스템 2

이번에는 저번의 인벤토리 시스템을 이어서 작성한다. 

인벤토리 슬롯을 클릭하거나 이동시키거나 디스플레이에 보이도록 하는 스크립트를 작성하였다.

 


1. InventoryDisplay 스크립트

InventoryDisplay는 인벤토리 UI를 관리하는 추상 클래스다. 이 클래스는 인벤토리 시스템(InventorySystem)과 UI 슬롯(InventorySlot_Ui)을 연결하고, UI 업데이트를 처리한다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.Events;


public abstract class InventoryDisplay : MonoBehaviour
{
    [SerializeField] InventoryMouseItemData mouseInventoryItem;


    protected InventorySystem inventorySystem;
    protected Dictionary<InventorySlot_Ui, InventorySlot> slotDictionary;

    public InventorySystem InventorySystem => inventorySystem;
    public Dictionary<InventorySlot_Ui, InventorySlot> SlotDictionary => slotDictionary;

    protected virtual void Start()
    {

    }

    public abstract void AssignSlot(InventorySystem invToDisplay);

    protected virtual void UpdateSlot(InventorySlot updateSlot)
    {
        foreach (var slot in slotDictionary)
        {
            if (slot.Value == updateSlot)
            {
                slot.Key.UpdateUISlot(updateSlot);
            }
        }
    }

    public void SlotClicked(InventorySlot_Ui clickedUISlot)
    {
        bool isShiftPressed = Keyboard.current.leftShiftKey.isPressed;
        if (clickedUISlot.AssignedInventorySlot.ItemData != null && mouseInventoryItem.AssignedInventorySlot.ItemData == null)
        {
            if (isShiftPressed && clickedUISlot.AssignedInventorySlot.SplitStack(out InventorySlot halfStackSlot)) // 분할할려고 할때
            {
                mouseInventoryItem.UpdateMouseSlot(halfStackSlot);
                clickedUISlot.UpdateUISlot();
                return;
            }
            else
            {
                mouseInventoryItem.UpdateMouseSlot(clickedUISlot.AssignedInventorySlot);
                clickedUISlot.ClearSlot();
                return;
            }
        }

        if (clickedUISlot.AssignedInventorySlot.ItemData == null && mouseInventoryItem.AssignedInventorySlot.ItemData != null)
        {
            clickedUISlot.AssignedInventorySlot.AssignItem(mouseInventoryItem.AssignedInventorySlot);
            clickedUISlot.UpdateUISlot();

            mouseInventoryItem.ClearSlot();
            return;
        }

        if (clickedUISlot.AssignedInventorySlot.ItemData != null && mouseInventoryItem.AssignedInventorySlot.ItemData != null)
        {
            bool isSameItem = clickedUISlot.AssignedInventorySlot.ItemData == mouseInventoryItem.AssignedInventorySlot.ItemData;
            if (isSameItem && clickedUISlot.AssignedInventorySlot.EnuoughRoomLeftInStack(mouseInventoryItem.AssignedInventorySlot.StackSize))
            {
                clickedUISlot.AssignedInventorySlot.AssignItem(mouseInventoryItem.AssignedInventorySlot);
                clickedUISlot.UpdateUISlot();

                mouseInventoryItem.ClearSlot();
            }
            else if (isSameItem &&
                !clickedUISlot.AssignedInventorySlot.EnuoughRoomLeftInStack(mouseInventoryItem.AssignedInventorySlot.StackSize, out int leftInStack))
            {
                if (leftInStack < 1) SwapSlot(clickedUISlot);
                else
                {
                    int remainingOnMouse = mouseInventoryItem.AssignedInventorySlot.StackSize - leftInStack;

                    clickedUISlot.AssignedInventorySlot.AddToStack(leftInStack);
                    clickedUISlot.UpdateUISlot();


                    var newItem = new InventorySlot(mouseInventoryItem.AssignedInventorySlot.ItemData, remainingOnMouse);
                    mouseInventoryItem.ClearSlot();
                    mouseInventoryItem.UpdateMouseSlot(newItem);
                    return;
                }
            }
            else if (!isSameItem)
            {
                SwapSlot(clickedUISlot);
                return;
            }
        }
    }

    private void SwapSlot(InventorySlot_Ui clickdeUISlot)
    {
        var cloneSlot = new InventorySlot(mouseInventoryItem.AssignedInventorySlot.ItemData, mouseInventoryItem.AssignedInventorySlot.StackSize);
        mouseInventoryItem.ClearSlot();

        mouseInventoryItem.UpdateMouseSlot(clickdeUISlot.AssignedInventorySlot);
        clickdeUISlot.ClearSlot();
        clickdeUISlot.AssignedInventorySlot.AssignItem(cloneSlot);
        clickdeUISlot.UpdateUISlot();
    }
}
  • AssignSlot 메서드: 이 추상 메서드는 인벤토리 시스템을 UI 슬롯과 연결하는 데 사용된다. 구체적인 연결 로직은 상속받은 클래스에서 구현한다.
  • UpdateSlot 메서드: 인벤토리 슬롯의 변경 사항을 UI에 반영한다. 이 메서드는 인벤토리의 상태가 변경될 때마다 호출된다.
  • SlotClicked 메서드: 사용자가 인벤토리 슬롯을 클릭할 때 호출된다. 이는 아이템을 선택하거나 이동하는 데 사용된다.

2. StaticInventoryDisplay 스크립트

StaticInventoryDisplayInventoryDisplay를 구체화한 클래스다. 이 클래스는 특정한 InventoryHolder에서 인벤토리 데이터를 가져와 UI에 표시한다.

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

public class StaticInventoryDisplay : InventoryDisplay
{
    [SerializeField] private InventoryHolder inventoryHolder;
    [SerializeField] private InventorySlot_Ui[] slots;

    protected override void Start()
    {
        base.Start();

        if (inventoryHolder != null)
        {
            inventorySystem = inventoryHolder.PrimaryInventorySystem;
            inventorySystem.OnIventorySlotChanged += UpdateSlot;
        }
        else Debug.LogWarning($"인벤토리에 할당되지 않았습니다 {this.gameObject}");

        AssignSlot(inventorySystem);
    }

    public override void AssignSlot(InventorySystem invToDisplay)
    {
        slotDictionary = new Dictionary<InventorySlot_Ui, InventorySlot>();

        if (slots.Length != inventorySystem.InventroySize) Debug.Log($"인벤토리 슬롯이 백엔드와 동기화 되지 않앗스빈다. {this.gameObject}");

        for (int i = 0; i < inventorySystem.InventroySize; i++)
        {
            slotDictionary.Add(slots[i],inventorySystem.InventorySlots[i]);
            slots[i].Init(inventorySystem.InventorySlots[i]);
        }
    }

}
  • Start 메서드: 인벤토리 시스템을 초기화하고, UI 슬롯을 설정한다.
  • AssignSlot 메서드: 인벤토리 시스템의 각 슬롯을 UI 슬롯과 연결한다. 이 메서드는 부모 클래스의 AssignSlot을 구체적으로 구현한다.

3. InventorySlot_Ui 스크립트

InventorySlot_Ui는 개별 인벤토리 슬롯의 UI를 관리한다. 이 클래스는 아이템의 이미지와 수량을 UI에 표시한다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;

public class InventorySlot_Ui : MonoBehaviour
{
    [SerializeField] private Image itemSprite;
    [SerializeField] private TextMeshProUGUI itemCount;
    [SerializeField] private InventorySlot assignedInventorySolt;

    private Button button;

    public InventorySlot AssignedInventorySlot
    {
        get { return assignedInventorySolt; }
        set { assignedInventorySolt = value; }
    }
    public Image ItemSprite
    {
        get { return itemSprite; }
        set { itemSprite = value; }
    }
    public InventoryDisplay ParentDisplay { get; private set; }

    private void Awake()
    {
        ClearSlot();

        button = GetComponent<Button>();
        button?.onClick.AddListener(OnUISlotClick);

        ParentDisplay = transform.parent.GetComponent<InventoryDisplay>();
    }

    public void Init(InventorySlot slot)
    {
        assignedInventorySolt = slot;
        UpdateUISlot(slot);
    }

    public void UpdateUISlot(InventorySlot slot)
    {
        if (slot.ItemData != null)
        {
            itemSprite.sprite = slot.ItemData.Icon;
            itemSprite.color = Color.white;

            if (slot.StackSize > 1) itemCount.text = slot.StackSize.ToString();
            else itemCount.text = "";
        }
        else
        {
            ClearSlot();
        }
    }

    public void UpdateUISlot()
    {
        if (assignedInventorySolt != null) UpdateUISlot(assignedInventorySolt);
    }

    public void ClearSlot()
    {
        assignedInventorySolt?.ClearSlot();
        itemSprite.sprite = null;
        itemSprite.color = Color.clear;
        itemCount.text = "";
    }

    public void OnUISlotClick()
    {
        ParentDisplay?.SlotClicked(this);
    }
}
  • UpdateUISlot 메서드: 지정된 인벤토리 슬롯의 정보를 UI에 표시한다.
  • ClearSlot 메서드: 슬롯을 비웠을 때 UI를 초기화한다.
  • OnUISlotClick 메서드: 슬롯이 클릭되었을 때 해당하는 동작을 수행한다. 이는 상위 InventoryDisplaySlotClicked 메서드를 호출한다

.

4. InventoryMouseItemData 스크립트

InventoryMouseItemData는 마우스로 드래그 중인 인벤토리 아이템을 관리한다. 이 스크립트는 드래그 중인 아이템의 이미지와 수량을 UI에 표시한다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.UI;
using TMPro;
using System;

public class InventoryMouseItemData : MonoBehaviour
{
    public Image ItemSprite;
    public TextMeshProUGUI ItemCount;
    public InventorySlot AssignedInventorySlot;

    private void Awake()
    {
        ItemSprite.color = Color.clear;
        ItemCount.text = "";
    }

    public void UpdateMouseSlot(InventorySlot invSlot)
    {
        AssignedInventorySlot.AssignItem(invSlot);
        ItemSprite.sprite = invSlot.ItemData.Icon;
        ItemCount.text = invSlot.StackSize.ToString();
        ItemSprite.color = Color.white;
    }

    private void Update()
    {
        if (AssignedInventorySlot.ItemData != null)
        {
            transform.position = Mouse.current.position.ReadValue();

            if (Mouse.current.leftButton.wasPressedThisFrame && !IsPointerOverUIObject())
            {
                ClearSlot();
            }
        }
    }

    public void ClearSlot()
    {
        AssignedInventorySlot.ClearSlot();
        ItemCount.text = "";
        ItemSprite.sprite = null;
        ItemSprite.color = Color.clear;
    }

    public static bool IsPointerOverUIObject()
    {
        PointerEventData eventDataCurruntPosition = new PointerEventData(EventSystem.current);
        eventDataCurruntPosition.position = Mouse.current.position.ReadValue();
        List<RaycastResult> results = new List<RaycastResult>();
        EventSystem.current.RaycastAll(eventDataCurruntPosition, results);
        return results.Count > 0;
    }
}
  • Update 메서드: 마우스의 위치를 추적하고, 아이템이 드래그되고 있을 때 해당 아이템을 화면에 표시한다. 마우스 클릭이 감지되면 드래그를 종료한다.
  • IsPointerOverUIObject 메서드: 현재 마우스 포인터가 UI 오브젝트 위에 있는지 확인한다.

 

반응형