내일배움캠프/TIL

팀프로젝트 제작 시작 - 크래프팅 시스템

서보훈 2024. 10. 31. 20:41

숙련주차 팀 프로젝트 - 생존게임 에서 아이템, 인벤토리, 자원 관련 부분을 당담하게 되었습니다.

인벤토리와 아이템 관련 내용 기반을 구현한 뒤, 가장 문제가 될것으로 예상되는 크래프팅 시스템 기초를 다뤄보았습니다.

 

먼저 아이템 데이터를 저장하는 스크립터블 오브젝트 입니다.

여기서 isCraftable 이 true일 경우 이 아이템은 제작이 가능한 아이템이며, 제작에 필요한 재료를 저장하는 직렬화 클래스 craftData 를 통해 어떤 재료가 얼마나 들어갈지를 정해주었습니다.

[CreateAssetMenu(fileName = "Item", menuName = "Item/New Item")]
public class ItemData : ScriptableObject
{
    [Header("Item Data")]
    public int itemId;
    public string itemName;
    public string discription;
    public Sprite icon;
    public ItemType itemType;
    public GameObject dropPrefab;

    [Header("Stack")]
    public bool isStack;
    public int maxStackAmount;

    [Header("Craftable")]
    public bool isCraftable;
    public MaterialData[] craftDatas;

    [Header("Consumable")]
    public bool isConsumable;
    public ConsumableDate[] consumableDatas;

    [Header("Equip")]
    public EquipType equipType;
    public GameObject equipPrefab;
}

[System.Serializable]
public class MaterialData
{
    public ItemData material;
    public int needCount;
}

 

실제 아이템을 제작할 CraftManager 입니다.

클래스이름은 임시로 지정해주어서 차후 변경될 수 있습니다.

public class CraftManager : MonoBehaviour
{
    public ItemDatabaseSO itemDatabase;

    private List<ItemData> craftItemData = new List<ItemData>();
    private Dictionary<int, int> playerItems = new Dictionary<int, int>();

    //TODO : 인벤토리 가져올 수단 - 파사드 패턴이면 좋을텐데
    private Inventory playerInven;

    //제작가능 아이템들 등록
    private void Awake()
    {
        foreach(ItemData item in itemDatabase.itemObjects)
        {
            playerItems.Add(item.itemId, 0);

            if(item.isCraftable)
            {
                craftItemData.Add(item);
            }
        }
    }

    public void OpenCraftMenu()
    {
        //제작 메뉴를 열 때, 플레이어 인벤토리의 아이템 정보를 확인
        for (int i = 0; i < playerInven.InvenItems.Count; i++)
        {
            ItemSlot itemSlot = playerInven.InvenItems[i];
            if (itemSlot.slotItem != null)
            {
                playerItems[itemSlot.slotItem.itemId] += itemSlot.itemCount;
            }
        }

        for(int i = 0; i < craftItemData.Count; i++)
        {
            if(CheckCraftable(craftItemData[i]))
            {
                //TODO : UI 조작 (제작 가능)
            }
            else
            {
                //TODO : UI 조작 (제작 불가능)
            }
        }
    }

    //아이템의 제작가능 여부 확인
    private bool CheckCraftable(ItemData craftItem)
    {
        for(int i = 0; i < craftItem.craftDatas.Length; i++)
        {
            if (playerItems[craftItem.craftDatas[i].material.itemId] < craftItem.craftDatas[i].needCount)
                return false;
        }

        return true;
    }
}

 

먼저, 모든 아이템의 정보를 가지고있는 아이템 데이터베이스로부터 isCraftable 이 true인 아이템을 가져와서 따로 리스트에 저장합니다.

public ItemDatabaseSO itemDatabase;

private List<ItemData> craftItemData = new List<ItemData>();
private Dictionary<int, int> playerItems = new Dictionary<int, int>();

//제작가능 아이템들 등록
private void Awake()
{
    foreach(ItemData item in itemDatabase.itemObjects)
    {
         playerItems.Add(item.itemId, 0);
         if(item.isCraftable)
        {
            craftItemData.Add(item);
        }
    }
}

 

이후 제작 메뉴를 열 때, 플레이어의 인벤토리의 모든 아이템의 갯수를 가져와서 아이템 ID를 키 값으로하는 딕셔너리에 저장합니다.

public void OpenCraftMenu()
{
    //제작 메뉴를 열 때, 플레이어 인벤토리의 아이템 정보를 확인
    for (int i = 0; i < playerInven.InvenItems.Count; i++)
    {
        ItemSlot itemSlot = playerInven.InvenItems[i];
        if (itemSlot.slotItem != null)
        {
            playerItems[itemSlot.slotItem.itemId] += itemSlot.itemCount;
        }
    }

    for(int i = 0; i < craftItemData.Count; i++)
    {
        if(CheckCraftable(craftItemData[i]))
        {
            //TODO : UI 조작 (제작 가능)
        }
        else
        {
            //TODO : UI 조작 (제작 불가능)
        }
    }
}

→ TIL 작성하면서 문듯 든 생각 : 인벤토리에 딕셔너리를 저장하고 아이템 들어올때마다 ID 확인해서 밸류값 증가시키는게 성능적으로 좋지 않을까? 제작 열때 딕셔너리 그대로 들고가서 활용하고...

 

이후 아이템 제작에 필요한 아이템의 아이디를 기반으로, 딕셔너리의 총 아이템 갯수를 확인하여 제작 가능여부를 결정합니다.

제작이 가능하면 UI에 아이템이 보이고, 그렇지 않다면 보이지 않습니다.

//아이템의 제작가능 여부 확인
private bool CheckCraftable(ItemData craftItem)
{
    for(int i = 0; i < craftItem.craftDatas.Length; i++)
    {
        if (playerItems[craftItem.craftDatas[i].material.itemId] < craftItem.craftDatas[i].needCount)
            return false;
    }

    return true;
}

아이템 제작 가능 여부를 확인하는 함수입니다.

아이템의 아이디를 키값으로, 해당 아이템의 소지 갯수를 가져와서 제작 가능 여부를 결정합니다.

 

 

개인적으로 테라리아처럼 아이템의 제작조건을 만족할경우 제작 UI에 아이템이 보이는식을 생각하고 만들었습니다.

 

다만... 기본적으로 해당 티어의 모든 제작 가능 아이템을 보여준 후, 아이템을 등록하여 제작하는 방식으로 변경될 수도 있을것 같습니다.