8년차 모션그래픽디자이너의 고군분투

[Unity Engine] 인벤토리 시스템 구현하기 -3- | 아이템 수집해서 인벤토리 창에 넣기 | C#, Inventory System, UI 연결하기, 인벤토리 매니저 스크립트 본문

코자이너/Unity

[Unity Engine] 인벤토리 시스템 구현하기 -3- | 아이템 수집해서 인벤토리 창에 넣기 | C#, Inventory System, UI 연결하기, 인벤토리 매니저 스크립트

쓰리디사람3Dperson 2024. 3. 20. 12:29
반응형

안녕하세요 오늘은 플레이어가 아이템을 수집할 때 인벤토리에 수집 되는 과정을 스크립트로 보여드리겠습니다.

using System;
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEditorInternal.Profiling.Memory.Experimental;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.UIElements;
using static UnityEditor.Progress;

public class InventoryManager : MonoBehaviour
{
    public static InventoryManager Instance;
    public List<ItemData> items = new List<ItemData>();
    public GameObject Inventory;
    public Transform itemContect;
    public GameObject InventoryItem;
    public List<ItemInventoryUI> ItemInventoryUISlots;
    public delegate void OnItemChanged();
    public static event OnItemChanged onItemChangedCallback;

    private void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(this.gameObject);
        }
        else
        {
            Instance = this;
            DontDestroyOnLoad(this.gameObject);
        }
    }

    private void Start()
    {
        ListItem();
        // 시작 할 때 아이템이 있으면 인벤토리 UI 업데이트 
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.I)) 
        {
            bool isActive = !Inventory.activeSelf;
            Inventory.SetActive(isActive); // 인벤토리 UI 활성화/비활성화 토글
                                           // 인벤토리가 활성화되면 마우스 커서를 표시하고, 그렇지 않으면 숨깁니다.
            UnityEngine.Cursor.visible = isActive;
            // 인벤토리가 활성화되면 마우스 커서를 잠그지 않고, 그렇지 않으면 잠급니다.
            UnityEngine.Cursor.lockState = isActive ? CursorLockMode.None : CursorLockMode.Locked;
            Time.timeScale = isActive ? 0 : 1;
        }
    }
    public void Add(ItemData newItem)
    {
        ItemData existingItem = items.Find(item => item.Name == newItem.Name);
        if (existingItem != null)
        {
            existingItem.Value += 1;
            // 같은 아이템이면 카운트 +1
        }

        else
        {
            newItem.Value = 1;
            items.Add(newItem);
            // 새로운 아이템이면 추가
        }
        onItemChangedCallback?.Invoke(); // 아이템 변경 이벤트 발생
    }

    public void Remove(ItemData item)
    {
        ItemData itemToRemove = items.Find(i => i.Name == item.Name);
        if (itemToRemove != null && itemToRemove.Value > 0)
        {
            itemToRemove.Value -= 1;
            int index = items.IndexOf(itemToRemove);
            ItemInventoryUISlots[index].countitemText.text = itemToRemove.Value.ToString();
            Debug.Log("포션이 깎여야함");

            if (itemToRemove.Value == 0)
            {
                Debug.Log("포션이 0이 되어야 함");
                ItemInventoryUISlots[index].gameObject.SetActive(false);
                items.Remove(itemToRemove);
            }

            onItemChangedCallback?.Invoke();
        }
    }
    public void ListItem()
    {
        foreach (Transform child in itemContect)
        {
            child.gameObject.SetActive(false);
            // 빈 슬롯 다 지우고
        }
        foreach (Transform child in itemContect)
        {
            if (!child.gameObject.activeSelf)
            // 빈 슬롯 상태에서
            {
                for (int i = 0; i < items.Count; i++)
                {
                // 아이템 먹은 만큼 슬롯 활성화하고 UI 업데이트
                    ItemInventoryUISlots[i].gameObject.SetActive(true);
                    ItemInventoryUISlots[i].itemIconNameText.text = items[i].Name;
                    ItemInventoryUISlots[i].itemNameText.text = items[i].Name;
                    ItemInventoryUISlots[i].itemDescriptionText.text = items[i].Description;
                    ItemInventoryUISlots[i].itemIconImage.sprite = items[i].Icon;
                    ItemInventoryUISlots[i].itemBigImage.sprite = items[i].BigImage;
                    ItemInventoryUISlots[i].countitemText.text = $"{items[i].Value}";
                    ItemInventoryUISlots[i].CurrentitemData = items[i];
                    // 슬롯에 커렌트 아이템을 넣어 이 아이템이 무엇인지 알게 해준다
                }
            }
        }
    }
}

이 아이템 매니저는 빈 오브젝트를 만들어서 거기에 넣어줍니다.

이번엔 슬롯에 넣어줄 스크립트를 작성합니다.

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using TMPro;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.PlayerLoop;
using UnityEngine.UI;
using static UnityEditor.Progress;

public class ItemInventoryUI
    : MonoBehaviour, IPointerEnterHandler
    // 인벤토리에서 마우스 감지
{
    public TextMeshProUGUI itemIconNameText;
    public TextMeshProUGUI itemNameText;
    public TextMeshProUGUI itemDescriptionText;
    public Image itemIconImage;
    public Image itemBigImage;
    public TextMeshProUGUI countitemText;
    public ItemData CurrentitemData;
    // 슬롯에서 들어가야 하는 변수들
    public void OnPointerEnter(PointerEventData eventData)
    {
        UpdateItemUI();
        // 마우스가 움직일때마다 인벤토리 상태창 UI를 업데이트 해준다
    }

    public void OnMouseDown()
    {
        if (CurrentitemData.Type == ItemType.Potion)
        {
            Debug.Log("포션 먹는다");
            InventoryManager.Instance.Remove(CurrentitemData);
            // 포션은 소비아이템, 갯수가 0이되면 사라진다
        }
        ChangeWeapon();
        // 무기와 방어구는 계속 인벤토리에 있으면서 교체
        Time.timeScale = 1.0f;
    }
    public void UpdateItemUI()
    {
        Inventory.instance.InventoryDescriptionUI.Refresh(CurrentitemData);
    }
    public void ChangeWeapon()
    {
        if (CurrentitemData == null)
        {
            return;
        }
        FindObjectOfType<Player>().ActivateItem(CurrentitemData);
        Inventory.instance.gameObject.SetActive(false);
        // 인벤토리에서 해당 무기를 누르면 그것으로 교체하는 함수
    }
}

슬롯에 들어가는 스크립트안에는 모노비헤비어옆에 IPointerEnterHandler를 넣어 마우스 입력값을 감지할 수 있도록 했습니다.

반응형

또한 변수명을 퍼블릭으로 두어 UI에 나타나야할 오브젝트들을 알맞게 넣어줍니다.

커렌트아이템 데이터는 인벤토리매니저에서  

public List<ItemData> items = new List<ItemData>();

이 코드에 쌓이는 데이터를 받아서 업데이트가 됩니다.

또한 온 클릭에 플러스를 누르고 슬롯에 들어가는 스크립트를 넣어서 OnMouseDown() 함수를 넣어줍니다.

 

제가 만든 인벤토리는 

이런식으로 왼쪽에는 아이템이 무엇인지 알려주고 가운데쯤에는 인벤토리 상태창을 만들어서 해당 아이템의 이미지와 그 해당 아이템의 이름과 설명이 자세히 나오는 식으로 구현했습니다.

    public void UpdateItemUI()
    {
        Inventory.instance.InventoryDescriptionUI.Refresh(CurrentitemData);
    }

그렇기 때문에 이 함수가 사용되었습니다. 이건 마우스를 올려놓는 값을 받으면 인벤토리 상태창이 업데이트 되는 함수입니다. 이걸 활용하기 위해서 다시 인벤토리에 스크립트를 하나 넣어주어야 합니다. 인벤토리 상태창은 슬롯마다 가지고 있는 것이 아니라 인벤토리에서 해당 아이템 값을 받아서 계속 재활용하며 업데이트 하는 식으로 구현해야 하기 때문입니다.

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

public class UI_InventoryDescription : MonoBehaviour
{
    public Image BigImageUI;
    public TextMeshProUGUI NameTextUI;
    public TextMeshProUGUI DescriptionTextUI;

    public void Refresh(ItemData itemData)
    {
        NameTextUI.text = itemData.Name;
        DescriptionTextUI.text = itemData.Description;
        BigImageUI.sprite = itemData.BigImage;
    }
}

인벤토리안에 인벤토리 스크립트는 나중에 설명드리겠습니다.

이런식으로 스크립트를 달아줍니다.

 

여기서 한번 끊고 이어 올리겠습니다. 이제 다음 글은 아이템에 픽업 스크립트를 달아 아이템이 플레이어를 향해 먹어지는는 과정을 구현해보겠습니다.

반응형
Comments