ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 스파르타3D서바이벌게임/Inventory만들기/유니티 숙련주차
    스파르타 게임 개발 2024. 6. 4. 15:08

    ItemSlot들을 갖고 있는 UIInventory 클래스를 만들어서 인벤토리 기능을 구현 해보도록 하자. 

     

    UIInvetory 오브젝트가 여러 버튼들을 관리 하는 것처럼 여러 아이템슬롯도 관리 할 수 있게 만드는 것이다. 그러기 위해서 ItemSlot의 객체를 UIInventory에서 생성을 하는 것이다.

     

    먼저 사진을 보는 것처럼 여러 버튼이 있는 걸 볼 수가 있다. 이걸 관리 할 수 있게 변수들을 만들어 주자.

     

    [Header("Select Item")]
    public TextMeshProUGUI selectedItemName;
    public TextMeshProUGUI selectedItemDescription;
    public TextMeshProUGUI selectedStatName;
    public TextMeshProUGUI selectedStatValue;
    public GameObject useButton;
    public GameObject equipButton;
    public GameObject unequipButton;
    public GameObject dropButton;

     

    이렇게 만들어줘서 선택 한 아이템의 Data의 정보로 Text가 변경이 되게 해줄 것이고, 여러 버튼들이 아이템타입에 맞춰서 활성화, 비활성화가 되게 해준다. 

     

    그리고 

        public ItemSlot[] slots;

    라는 코드를 작성 해줘서 ItemSlot들을 관리 할 수 있게 한다. 

     

    public GameObject inventoryWindow;
    public Transform slotPanel;
    public Transform dropPosition;

    그리고 위 코드를 선언을 해주는데 여기서 inventoryWindow는 Object를 껏다 켰다 해야하고, 여러 자식 객체들을 관리 해야 하므로 GameObject로 선언을 해준다. 그리고 slotPanel 같은 경우는 true인지 false인지 값을 초기화만 해줄 것인데 이것은 Transform만 선언을 해줘도 된다. 그러므로 Transform으로 만들어준다. 

     

    **slotPanel을 Transform으로 가져오는 이유는 해당 변수가 단순히 슬롯이 배치된 패널의 위치 정보만을 필요로 하기 때문이다. 실제로 슬롯 패널을 활성화하거나 비활성화하는 등의 작업은 UI 요소에 대한 직접적인 조작이 아니라, 해당 패널의 위치 및 계층 구조에 대한 정보만 필요로 한다.

    따라서 Transform 타입으로 선언하여 패널의 위치 및 계층 구조를 쉽게 참조할 수 있다. 만약 게임 오브젝트를 직접 참조한다면 불필요한 UI 조작 메서드에 접근할 수 있어서 실수할 가능성이 높아짐.

    또한, Transform을 사용하면 손쉽게 슬롯 패널의 하위 오브젝트에 접근하여 슬롯들을 초기화하거나 배치할 수 있다. 이는 코드를 보다 간결하게 작성하고 유지보수하기 쉽게 만들어 준다고 한다. **

     

    그리고 클릭 한 아이템의 Data를 담아줄 private ItemData selectedItem; 변수도 만들어 주면 된다. 

     

    이제 Start 함수에서 기본적인 초기화를 하는 코드를 작성 해준다. 

     

    private void Start()
    {
        controller = CharacterManager.Instance.Player.controller;
        condition = CharacterManager.Instance.Player.condition;
        dropPosition = CharacterManager.Instance.Player.dropPosition;

        controller.inventory += Toggle;
        CharacterManager.Instance.Player.addItem += AddItem;

        inventoryWindow.SetActive(false);
        slots = new ItemSlot[slotPanel.childCount];

        for(int i = 0; i < slots.Length; i++)
        {
            slots[i] = slotPanel.GetChild(i).GetComponent<ItemSlot>();
            slots[i].index = i;
            slots[i].inventory = this;
        }
        ClearSelectedItemWindow();  
    }

     

    이렇게 코드를 작성 해주면 된다. 값을 초기화 해주는 부분이 있고, Event에 함수를 넣는 코드도 있다. inventoryWindow.SetActive(false); 코드는 현재 오브젝트를 켜뒀기 때문에 시작하면 인벤토리 꺼두기 위함이다. 

     

    그리고 slots를 초기화를 해주는데 slotPanel은 만들어둔 ItmeSlot들을 갖고 있는 grid 컴포넌트가 있는 빈 객체다. 

     

    여기에 아이템슬롯들을 만들어 뒀으니 그 자식객체의 수만큼 배열을 만든다. 

     

    그리고 

     for(int i = 0; i < slots.Length; i++)
        {
            slots[i] = slotPanel.GetChild(i).GetComponent();
            slots[i].index = i;
            slots[i].inventory = this;
        }

    반복문을 통해서 아이템슬롯들마다 초기화를 하게 해준다. index는 필드에 선언 돼있는 값, inventory는 해당 클래스를 넣으므로 아이템슬롯의 모든 배열에 이 inventory로 초기화를 하게 해준다. 그래서 같이 묶여 있을 수 있게 한다. 

    slots[i] = slotPanel.GetChild(i).GetComponent();은 i번째의 맞는 아이템슬롯에 컴포넌트를 넣게 한다. 그러니까 slots[1]번째 객체에는 slotPanel에 만들어둔 [1]번째의 객체정보가 들어가게 되는 것이다. 이 코드들은 클래스로써 주소를 넘겨받는 그러니까 slots이 변경 되면 slotPanel.GetChild(i)도 같이 변경이 된다. 

     

    그러니까 slots는 slotPanel.GetChild(i)가 되는 것이다. 

     

    이렇게 연결을 해주고, 그 다음에 

     

      private void ClearSelectedItemWindow()
      {
          selectedItemName.text = string.Empty;
          selectedItemDescription.text = string.Empty;
          selectedStatName.text = string.Empty;
          selectedStatValue.text = string.Empty;

          useButton.SetActive(false);
          equipButton.SetActive(false);
          unequipButton.SetActive(false);
          dropButton.SetActive(false);
      }

     

    이 함수를 호출 시켜서 Inventory에 있는 버튼과 text를 비워준다. 초기작업이니까.

     

    그 다음에 이제 아이템을 상호작용 했을 때 가방에 들어오게 하는, ItemSlot에 넣어주는 코드를 작성 해준다. 

     

    AddItem 함수를 만들어 준다. 

    private void AddItem()
    {
        ItemData data = CharacterManager.Instance.Player.itemData;

        if ( data.canStack)
        {
            ItemSlot slot = GetItemStack(data);
            if (slot != null) 
            {
                slot.quantity++;
                UpdateUI();
                CharacterManager.Instance.Player.itemData = null;
                return;
            }
        }

        ItemSlot emptySlot = GetEmptySlot();

        if (emptySlot != null)
        {
            emptySlot.item = data;
            emptySlot.quantity = 1;
            UpdateUI();
            CharacterManager.Instance.Player.itemData = null;
            return;
        }

        ThrowItem(data);
        CharacterManager.Instance.Player.itemData = null;
    }

     

    이 AddItem은 이벤트로 싱글턴에 만들어둔 Envent에 추가 시켜서 우리가 아이템에 E키를 눌러 상호작용을 했을 때 호출을 하게 하는 것이다. 코드를 보면 ItemData data = CharacterManager.Instance.Player.itemData; 라고 되어 있는데 우리는 상호작용을 누른 아이템의 data가 저기에 저장이 되게 해놓았다.  그리고 다음으로 슬롯이 비었는지를 확인 하게 하는 코드다. 아이템이 비어 있는 슬롯을 찾아서 그 슬롯에 저장을 하게 하는 것이다. 

     

     private ItemSlot GetEmptySlot()
     {
         for (int i = 0; i < slots.Length; i++)
         {
             if (slots[i].item == null)
             {
                 return slots[i];
             }
         }

         return null;
     }

     

    코드를 보면 배열의 길이만큼 반복문을 실행 하고 비어있는 슬롯을 찾아서 반환한다. 

     

    그리고 반환이 된 슬롯에 싱글턴에 저장 된 아이템Data를 저장한다. 그리고 다시 null로 만들어서 싱글턴에 변수를 비워둔다. 그리고 만약 저장을 하려는 아이템이 자원으로써 여러개 축적이 가능한 경우 

     

     if ( data.canStack)
        {
            ItemSlot slot = GetItemStack(data);
            if (slot != null) 
            {
                slot.quantity++;
                UpdateUI();
                CharacterManager.Instance.Player.itemData = null;
                return;
            }

     

     

     private ItemSlot GetItemStack(ItemData data)
     {
         for (int i = 0; i < slots.Length; i++)
         {
             if(slots[i].item == data && slots[i].quantity < data.maxStackAmount)
             {
                 return slots[i];
             }
         }
         return null;
     }

    라는 코드를 작성 해서 자막의 숫자가 증가시키게 만들어 준다. maxStackAmout가 넘었을 경우 저장 X

     

    private void UpdateUI()
    {
        for (int i = 0; i < slots.Length; i++)
        {
            if(slots[i].item != null)
            {
                slots[i].Set();
            }
            else
            {
                slots[i].Clear() ;
            }
        }
    }

    --UpdateUI.코드다.

     

    public void Set()
    {
        icon.gameObject.SetActive(true);
        icon.sprite = item.icon;
        quantityText.text = quantity > 1 ? quantity.ToString() : string.Empty;

        if (outline != null)
        {
            outline.enabled = equipped;
        }
    }

    public void Clear()
    {
        item = null;
        icon.gameObject.SetActive(false);
        quantityText.text = string.Empty;
    }

     

    Set과 Clear의 코드. ItemSlot의 오브젝트를 설정 해준다. 

     

    만약 슬롯을 다 찾고 저장 할 곳이 없다면 아이템을 그냥 버리게 한다. 

    ThrowItem(data); 라는 코드를 작성 해주고, 아이템을 버렸으니 싱글턴에 아이템Data도 다시 null로 해준다. 

     

      private void ThrowItem(ItemData data) 
      {
          Instantiate(data.dropPrefab, dropPosition.position, Quaternion.Euler(Vector3.one * Random.value * 360));
      }

     

    ThrowItem 함수. 싱글턴에 있는 아이템Data로 오브젝트를 만들고 싱글턴에 있는 변수는 다시 null로 만드는 것이다.

     

    이렇게 하면 우리는 아이템을 주웠을 때 인벤토리에 등록을 하게 할 수가 있게 된다. 

     

    그리고 이제 인벤토리에 있는 아이템을 사용 하는 기능을 추가 해보자. 

Designed by Tistory.