본문 바로가기

Unity

유니티 (Unity) - 실전 인벤토리 제작 (4부)

반응형

 

4부에서는 인벤토리에 들어 있는 아이템을 Json Data로 저장해 보겠습니다.

 

UniTask를 사용할 일이 없을 것 같아 3부에서 코루틴으로 사용해도 된다고 했었지만

파일 불러오기에서 사용해야 될 일이 있어 설치하셔야겠습니다.

 

https://github.com/Cysharp/UniTask

 

GitHub - Cysharp/UniTask: Provides an efficient allocation free async/await integration for Unity.

Provides an efficient allocation free async/await integration for Unity. - Cysharp/UniTask

github.com

 

위 링크로 가시면 UniTask를 설치하실 수 있습니다.

 

오늘도 여기저기 추가되는 코드가 있기에 집중해서 따로 오시기 바랍니다.

우선 인벤토리를 파일로 저장하기 위한 데이터 모델을 하나 만들겠습니다.

 

using System.Collections.Generic;

[System.Serializable]
public class InventoryModel
{
    public List<string> invenItemID = new();
    public List<int> invenArrayNumber = new();
    public List<int> invenItemCount = new();
}

 

Json 파일로 저장해야 하기에 InventoryModel이 직열화 되어 있습니다.

invenItemID -> blue와 red 아이템을 만들 때 사용한 아이템의 ID가 저장됩니다.

invenArrayNumber -> 인벤토리에 아이템이 위치하고 있는 슬롯의 번호가 저장됩니다.

invenItemCount -> 아이템의 수량이 저장됩니다.

 

다시 InventoryManager.cs에 추가 코드입니다.

    public void JsonDataInventory(Item item, int slotNumber, int itemCount)
    {
        var g = Instantiate(inventoryItem);
        g.transform.SetParent(inventorySlots[slotNumber].transform);
        g.transform.localScale = new Vector3(1, 1, 1);
        g.SetActive(true);
        g.GetComponent<InventoryItem>().InitialiseItem(item, itemCount);
    }

 

게임을 시작한 후 Json Data를 불러와 인벤토리에 저장된 Data의 아이템을 배치하게 해 줍니다.

코드 설명은 따로 하지 않겠습니다. 아이템 Add 코드와 별반 다르지 않습니다.

 

다음은 DataManager.cs 에 추가 코드입니다.

    [Header("아이템 목록")]
    public Item[] items;

    private string inventoryDataPath;
    readonly string inventoryFileName = "inventory.json";

 

Item [] items; -> 아이템 목록입니다. 앞에서 만든 blue와 red아이템을 여기에 등록해 주세요.

그리고 추가되는 아이템이 있다면 그 아이템들도 여기 목록에 추가해 주셔야 합니다.

이 아이템 목록의 역할은 Json Data에서 불러온 아이템 ID와 비교해서 

같은 아이템이 있다면 items에서 아이템의 데이터를 인벤토리로 넣어 주는 역할을 하게 됩니다.

inventoryDataPath -> Json 파일이 저장될 Path

inventoryFileName -> Json의 파일 이름입니다.

계속 DataManager.cs에 이어지는 추가 코드입니다.

 

    private void Start()
    {
#if UNITY_EDITOR
        inventoryDataPath = Path.Combine(Application.dataPath, "JsonData", inventoryFileName);
#elif UNITY_ANDROID
        inventoryDataPath = Path.Combine(Application.persistentDataPath , inventoryFileName);
#endif

        if (File.Exists(inventoryDataPath))
            // Json 데이터 불러오기
            LoadData().Forget();
        }
    }

 

우선 유니티의 Project > Assets에 JsonData라는 폴더를 하나 만들어 주세요.

이곳에 파일이 저장되게 될 것입니다.

 

위 코드에서 Path를 얻는 방법이 두 가지가 나오는데 하나는 유니티 에디터 상에서의 Path

즉 방금 만든 JsonData 폴더의 위치를 받아 오는 것이고

두 번째는 안드로이드 일 때 Path를 받아 오는 방식입니다.

이건 공식과 같은 거라 그냥 이렇게 받아온다라고 알고 계시면 됩니다.

바로 밑 if 문의 역할은 Json 파일이 존재한다면 데이터를 불러옵니다.

 

  private async UniTask LoadData()
    {
        InventoryModel inventoryModel;

        inventoryModel = await DataFileLoad<InventoryModel>(inventoryDataPath);
        if (inventoryModel != null)
        {
            for (int i = 0; i < inventoryModel.invenItemID.Count; i++)
            {
                var index = Array.FindIndex(items, item => item.itemID == inventoryModel.invenItemID[i]);
                inventory.JsonDataInventory(items[index],
                    inventoryModel.invenArrayNumber[i],
                    inventoryModel.invenItemCount[i]
                );
            }
        }
    }

    public void SaveData()
    {
        InventoryModel inventoryModel = new();

        // 인벤토리 저장
        if (inventory.inventorySlots.Count > 0)
        {
            for (int i = 0; i < inventory.inventorySlots.Count; i++)
            {
                var slotItem = inventory.inventorySlots[i].GetComponentInChildren<InventoryItem>();
                if (slotItem != null)
                {
                    inventoryModel.invenItemID.Add(slotItem.item.itemID);
                    inventoryModel.invenArrayNumber.Add(i);
                    inventoryModel.invenItemCount.Add(slotItem.count);
                }
            }
            DataFileSave(inventoryModel, inventoryDataPath);
        }
    }

 

실제 Data를 파일로 저장하고 저장된 파일을 읽어오는 코드는 바로 밑에 있으니 참고하세요.

 

        InventoryModel inventoryModel;

        inventoryModel = await DataFileLoad<InventoryModel>(inventoryDataPath);

 

인벤토리 모델을 하나 생성 하고 DataFileLoad로 Json 데이터를 읽어와 inventoryModel에 넣어 줍니다.

 

            for (int i = 0; i < inventoryModel.invenItemID.Count; i++)
            {
                var index = Array.FindIndex(items, item => item.itemID == inventoryModel.invenItemID[i]);
                inventory.JsonDataInventory(items[index],
                    inventoryModel.invenArrayNumber[i],
                    inventoryModel.invenItemCount[i]
                );
            }

 

불러온 Json Data의 Count에 맞게 for 문을 돌려 위에서 만든 items라는 아이템 목록의 아이템 ID와

불러온 Json Data Item ID를 비교 후 같은 ID가 있다면 items 목록의 index 값을 넘겨줍니다.

그럼 위에 InentoryManager에서 만든 JsonDataInventory 메서드를 이용해 해당 아이템을

인벤토리에 배치해 줍니다.

여기서 중요한 게 아이템을 저장할 때 여러 Slot 칸 중에 해당 아이템이 10번  Slot에 있었다면

여기서 아이템을 배치할 때도 역시 10번 Slot에 배치되어야 할 것입니다.

g.transform.SetParent(inventorySlots[slotNumber].transform);

JsonDataInventory 메서드에서 이 기능이 그 역할을 해주고 있습니다.

생성된 아이템을 인벤토리 Slot 목록 중 invenArrayNumber 값을 이용해 해당 위치의 Slot 위치값을 

생성된 아이템의 SetParent에 전달하게 되면 그 위치로 가게 되는 것입니다.

 

저장은 등록된 Slot 목록을 불러와 자식으로 있는 InventoryItem이 있는지 검사 후

해당 아이템의 Id와 슬롯의 위치값 그리고 수량을 인벤토리 모델에 넣어 파일로 저장하는 부분입니다.

 

   public async UniTask<T> DataFileLoad<T>(string _path) where T : class
    {
        try
        {
            if (!File.Exists(_path))
            {
                return null;
            }

            using (var reader = new StreamReader(_path))
            {
                string content = await reader.ReadToEndAsync();
                return JsonUtility.FromJson<T>(content);
            }
        }
        catch (Exception ex)
        {
            Debug.LogError($"File Read Error: {ex.Message}");
            return null;
        }
    }

    public void DataFileSave<T>(T data, string _path) where T : class
    {
        try
        {
            string directoryPath = Path.GetDirectoryName(_path);

            if (!Directory.Exists(directoryPath))
            {
                Debug.LogError($"Directory does not exist: {directoryPath}");
                return;
            }

            string saveJsonData = JsonUtility.ToJson(data, true);
            File.WriteAllText(_path, saveJsonData);
        }
        catch (Exception ex)
        {
            Debug.LogError($"File Save Error: {ex.Message}");
        }
    }

 

파일로 저장하고 불러오는 코드의 설명은 생략하겠습니다.

이것 역시 공식과 같은 것이라 그냥 붙여 넣기 하시면 되는 부분입니다.

다만 한 가지 메서드들이 제네릭 타입으로 되어 있습니다. 

이건 향후에 게임데이터를 저장할 때 인벤토리 데이터만 저장할게 아니기에 여러 데이터 모델을

같이 사용해야 합니다. 그때를 위해 제네릭으로 받아 오는 것입니다.

사용 방법은 위 코드에도 있지만 다시 한번 적어 본다면

읽어 올 땐 

await DataFileLoad<InventoryModel>(inventoryDataPath);

 

데이터 모델 타입을 위 코드처럼 적어 주시면 됩니다.

 

DataFileSave(inventoryModel, inventoryDataPath);

 

데이터를 저장할 땐 모델 타입과 Path를 위 코드처럼 같이 넣어 주시면 됩니다.

 

마지막으로 작동이 잘되나 확인해 보겠습니다.

 

 

위 이미지처럼 Save Button을 하나 만들어 Click 이벤트에 DataManager의 SaveData 메서드와 연결해 주세요.

그리고 JsonData 폴더가 생성되어 있나 확인하신 후 게임을 플레이시키신 후

아이템을 생성하시고 Save 버튼을 눌러보시기 바랍니다

 

 

유니티 JsonData에 마우스 오른쪽 버튼을 클릭 후 Refresh를 클릭하시면 방금 저장된 Json Data가 보일 것입니다.

 

{
    "invenItemID": [
        "97fd1ce0-6d64-43ac-ae65-3a0c432dcb1a",
        "88ffc417-478e-467a-9c82-ba67b7a20419"
    ],
    "invenArrayNumber": [
        0,
        7
    ],
    "invenItemCount": [
        1,
        1
    ]
}

 

이렇게 저장되게 됩니다.

이제 이 데이터를 가지고 게임을 실행했을 때 제대로 불러와지는지 다시 확인해 보겠습니다.

 

 

위 JsonData에 맞게 Slot 0번 위치에 하나 그리고 슬롯 7번 위치에 하나씩 잘 표시되고 있네요

 

4부는 여기까지 하겠습니다.

5부에선 아이템을 클릭하면 해당 아이템의 정보가 표시되는 창과 아이템을 사용해서 소비되는 기능

다 사용된 아이템을 삭제하는 기능까지 만들어 보겠습니다.

 

반응형