말감로그

Unity - 아이템 리팩토링 / 인벤토리 & Toolbar 간 슬롯 이동 본문

카테고리 없음

Unity - 아이템 리팩토링 / 인벤토리 & Toolbar 간 슬롯 이동

habbn 2024. 10. 18. 00:26
728x90

 

아이템 리팩토링 확장하기

 

현재는 RICE_SEED와 TOMATO_SEED 아이템 뿐이다. 하지만 추후에 물뿌리개, 도끼 등등 더 많은 아이템을 확장하기 위해서는 스크립트를 변경해야 한다.

아이템 데이터를 정의하기 위해 ScriptableObject를 사용한 클래스를 만들어준다.

 

ScriptableObject는 Unity에서 데이터를 관리하고 저장하는 데 사용되는 클래스로, 일반적으로 게임의 상태를 저장하지 않고 데이터 자산(assed)처럼 여러 객체에서 공통으로 사용할 수 있는 데이터를 정의할 때 유용하다.

 

우선 ItemData 스크립트를 생성한다.

 

 

 

[CreateAssetMenu]는 Unity의 에디터에서 ScriptableObject를 쉽게 생성할 수 있도록 도와주는 속성이다.

// fileName = 기본 파일 이름 , menuName = Unity에디터에서 우클릭했을때 나타나는 메뉴 이름
// order =  메뉴에서 표시되는 순서, 숫자가 낮을수록 위쪽에 표시
[CreateAssetMenu(fileName = "Item Data", menuName = "Item Data", order = 50)]
public class ItemData : ScriptableObject
{
    public string itemName = "Item Name";
    public Sprite icon;
}

 

 

Unity에디터로 돌아가서 Project > 우클릭 > Create를 누르면 ItemData 항목이 생긴다

 

각 아이템에 맞게 ItemName과 Icon을 설정해주면 된다. 

이렇게함으로써 여러가지 아이템 프리팹을 생성한 후 그 아이템에 맞는 Item Data를 만들어 넣어주면된다.

 

 

그리고 기존의 Collectable 스크립트와 Item 스크립트를 분리하여 코드의 구조화와 재사용성을 높였다.

 

기존 Collectable.cs

using UnityEngine;

public enum CollectableType { NONE, RICE_SEED }

public class Collectable : MonoBehaviour
{
    public CollectableType type;
    public Sprite icon;
    public Rigidbody2D rigid;

    void Start()
    {
        rigid = GetComponent<Rigidbody2D>();
    }

    void OnTriggerEnter2D(Collider2D other)
    {
        Player player = other.GetComponent<Player>();

        if (player)
        {
            player.inventory.Add(this);
            Destroy(this.gameObject);
        }
    }
}

 

수정된 Collecatble.cs

[RequireComponent(typeof(Item))]
public class Collectable : MonoBehaviour
{
    void OnTriggerEnter2D(Collider2D other)
    {
        Player player = other.GetComponent<Player>();

        if (player)
        {
            Item item = GetComponent<Item>();

            if (item != null)
            {
                player.inventory.Add(item);
                Destroy(this.gameObject);
            }
        }
    }
}

 

ItemData ScriptObjectable로 아이템 데이터를 관리하기 때문에 CollectableType을 지우고

오직 플레이어와의 상호작용만을 담당하게 한다. 아이템이 수집될 때 어떤 동작을 해야하는지에 대한 로직을 포함한다.

 

RequireComponent(typeof(Item)) 는 이 스크립트가 사용될 때 반드시 Item 컴포넌트가 포함되어야 한다는 것을 말한다.

 

 

그리고 Item 스크립트에서 아이템 정보와 물리적 속성을 관리하게 한다.

[RequireComponent(typeof(Rigidbody2D))]
public class Item : MonoBehaviour
{
    public ItemData data;

    [HideInInspector] public Rigidbody2D rigid;

    void Start()
    {
        rigid = GetComponent<Rigidbody2D>();
    }
}

 

이렇게 변경해줬더니 해당 오류가 발생하면서 기존의 실행되던 아이템 드롭이 실행되지 않았다.

 

확인해보니 rigidbody2D가 할당되지 않아 AddForce에서 오류가 발생했던 것이었다.

public void DropItem(Item item)
{
    Vector3 spawnLocation = transform.position;

    Vector3 spawnOffset = Random.insideUnitCircle * 2f;

    Item droppedItem = Instantiate(item, spawnLocation + spawnOffset, Quaternion.identity);

    droppedItem.rigid.AddForce(spawnOffset * 0.3f, ForceMode2D.Impulse);
}

 

분명 rigidbody2D 컴포넌트가 할당이 잘 되어있는데 왜 오류가 뜨는거지 하고 확인해봤더니 호출 순서 때문이었다.

rigidbody가 할당되기 전에 DropItem()이 호출되어 실행되었기 때문이다. 

 

그래서 Start() 를 Awake()로 변경하여 가장 먼저 호출되도록 해결하였다.

초기화 같은 경우는 Awake()에서 실행되야 함!!

[RequireComponent(typeof(Rigidbody2D))]
public class Item : MonoBehaviour
{
    public ItemData data;

    [HideInInspector] public Rigidbody2D rigid;

    void Awake()
    {
        rigid = GetComponent<Rigidbody2D>();
    }
}

 

이렇게 스크립트를 변경해줬기 때문에 Inventory_UI, Inventory ,Player 스크립트 등에서 CollectableType으로 설정되었던 것들은 모두 Item으로 변경해줘야 한다.

 

기존엔 아이템을 드랍하는 거까지 했으면 이젠 Inventory의 슬롯을 이동시키고 또 Toolbar로 이동시킬 수 있게 해야 한다.

// Inventory_UI.cs
public void SlotDrop(Slot_UI slot)
{
    if (UIManager.dragSingle)
    {
        UIManager.draggedSlot.inventory.MoveSlot(UIManager.draggedSlot.slotID, slot.slotID, slot.inventory);
    }
    else
    {
        UIManager.draggedSlot.inventory.MoveSlot(UIManager.draggedSlot.slotID, slot.slotID, slot.inventory, 
                                                    UIManager.draggedSlot.inventory.slots[UIManager.draggedSlot.slotID].count);
    }
    GameManager.instance.uiManager.RefreshAll();
}

 

드래그한 슬롯의 SlotID를 Drop한 Slot으로 Move시켜야 한다.

// Inventory.cs
public void MoveSlot(int fromIndex, int toIndex, Inventory toInventory, int numToMove = 1)
{
    Slot fromSlot = slots[fromIndex];
    Slot toSlot = toInventory.slots[toIndex];

    if (toSlot.isEmpty || toSlot.CanAddItem(fromSlot.itemName))
    {
        for(int i = 0; i < numToMove; i++)
        {
            toSlot.AddItem(fromSlot.itemName, fromSlot.icon, fromSlot.maxAllowed);
            fromSlot.RemoveItem();
        }
    }
}

 

점점 스크립트가 많아지면서 헷갈려지고 있다. 정리해보자면

 

Inventory.cs -> 인벤토리 시스템 구현

Inventory_UI.cs -> 개별 인벤토리 UI 담당

InventoryManager.cs -> 게임 내 다양한 인벤토리 관리 (Backpack, Toolbar)

 

 

 

Toolbar도 숫자 1~9를 누르면 해당 슬롯의 하이라이트가 되게 설정하고 마찬가지로 Toolbar_slot에도 Slot_UI 스크립트와 Event Trigger 컴포넌트를 달아주면 Inventory와 마찬가지로 슬롯간 이동이 가능하게 된다.

 

 

 

 

728x90