오늘은 만들어둔 시스템의 데이터를 저장하고 불러오는 기능을 구현해보자. 

우선 Save를 관리해줄 SaveManager를 만들어주자. 경로는 기본경로로 해주고 저장방식은 Json으로 저장해주도록 하자.

SaveManager.cs

public class SaveManager
{
    public void Init()
    {
        // 경로 설정
        _defaultPath = Path.Combine(Application.persistentDataPath, "saveData.json");

        // 디렉터리 확인 및 생성
        string directoryPath = Path.GetDirectoryName(_defaultPath);
        if (!Directory.Exists(directoryPath))
        {
            Directory.CreateDirectory(directoryPath);
        }

        Debug.Log($"Save File Path Initialized: {_defaultPath}");

    }
}

 

그리고 지금까지 만들어둔 시스템에서 저장할 부분을 직렬화방식으로 저장하기 위해 SaveData를 따로 만들어주자. 저장해야할 부분은 Inventory,Quest,Player 데이터를 저장하면 된다.

 

SaveData.cs

using System;
using System.Collections.Generic;
using UnityEngine;

[System.Serializable]
public class SaveData
{
    public List<SerializedItemSlot> inventoryData; // 인벤토리 데이터
    public PlayerData playerData;                 // 플레이어 데이터
    public List<SerializedQuestData> questData;             // 퀘스트 데이터
}

[System.Serializable]
public class PlayerData
{
    public Vector3 position; // 플레이어 위치
    public Vector3 rotation; // 플레이어 방향
    public float health;     // 플레이어 체력
}

[System.Serializable]
public class SerializedItemSlot
{
    public int itemID;   // 아이템 ID
    public int quantity; // 아이템 개수
}

[System.Serializable]
public class SerializedQuestData
{
    public int questID;       // 퀘스트 ID
    public string questState; // 퀘스트 상태
}

 

이렇게 하고 저장하는 부분을 구현해볼텐데 저장은 직렬화로 구현된 부분을 각 객체의 함수로 추가하고 마지막에 SaveManager에서 Json으로 저장하도록하자

Player.cs

// 저장
public void SavePlayer(ref SaveData saveData)
{
    saveData.playerData = new PlayerData
    {
        position = transform.position,
        rotation = transform.eulerAngles,
        health = currentHealth
    };
}

QuestManager.cs

 public void SaveQuests(ref SaveData saveData)
 {
     List<SerializedQuestData> serializedQuests = new List<SerializedQuestData>();
     foreach (var quest in Quests.Values)
     {
         serializedQuests.Add(new SerializedQuestData
         {
             questID = quest.questId,
             questState = quest.questState.ToString()
         }); 
     }
     saveData.questData = serializedQuests;
 }

InventoryManager.cs

 // 저장
 public void SaveInventory(ref SaveData saveData)
 {
     List<SerializedItemSlot> serializedInventory = new List<SerializedItemSlot>();
     foreach (var slot in inventory)
     {
         serializedInventory.Add(new SerializedItemSlot
         {
             itemID = slot.itemData.itemID,
             quantity = slot.quantity
         });
     }
     saveData.inventoryData = serializedInventory;
 }

SaveManager.cs

  public void SaveData()
  {
      SaveData saveData = new SaveData();

      Managers.Inventory.SaveInventory(ref saveData);
      Managers.Game.GetPlayer().GetComponent<Player>().SavePlayer(ref saveData);
      Managers.Quest.SaveQuests(ref saveData);

      string json = JsonUtility.ToJson(saveData, true);
      File.WriteAllText(_defaultPath, json);
      Debug.Log($"게임 데이터가 저장되었습니다: {_defaultPath}");
  }

 

이렇게 만들고 게임 데이터를 가져오는 기능을 만들어주자. 이때 만약 저장데이터가 없다면 기본 데이터를 저장해주고 

다른 시스템을 초기화해주는 기능도 넣어주도록 하자.

SaveManager.cs

public void LoadData()
{
    if (!File.Exists(_defaultPath))
    {
        Debug.LogWarning("저장된 데이터가 없습니다. 초기 데이터를 생성합니다.");

        // 초기 데이터를 생성
        CreateInitialData();
        SaveData(); // 초기 데이터를 저장
        return;
    }
    string json = File.ReadAllText(_defaultPath);
    SaveData saveData = JsonUtility.FromJson<SaveData>(json);

    SpawnPlayer();

    Managers.Quest.Init();
    Managers.Inventory.LoadInventory(saveData);
    Managers.Game.GetPlayer().GetComponent<Player>().LoadPlayer(saveData);
    Managers.Quest.LoadQuestsFromSaveFile(saveData);

    Debug.Log("게임 데이터가 불러와졌습니다.");
}

private void CreateInitialData()
{
   SpawnPlayer();

    // 인벤토리 초기화
    Managers.Inventory.Init();
    //Managers.Inventory.AddItem(Resources.Load<ItemData>("Items/Potion"), 5); // 기본 아이템 추가
    //Managers.Inventory.AddItem(Resources.Load<ItemData>("Items/Sword"), 1); // 기본 무기 추가

    // 플레이어 초기화
    var player = Managers.Game.GetPlayer().GetComponent<Player>();
    player.transform.position = Vector3.zero; // 기본 위치
    player.CurrentHealth = 100f; // 기본 체력
    player.index = 0; // 초기 퀘스트 인덱스

    // 플레이어 정보 출력
    Debug.Log("=== 플레이어 초기화 ===");
    Debug.Log($"위치: {player.GetComponent<Player>().transform.position}");
    Debug.Log($"체력: {player.GetComponent<Player>().CurrentHealth}");
    Debug.Log($"퀘스트 진행 상태: {player.GetComponent<Player>().index}");

    // 퀘스트 초기화
    Managers.Quest.Init();
}

public void SpawnPlayer()
{
    GameObject player = Managers.Game.Spawn(Define.WorldObject.Player, "Player");
    CinemachineVirtualCamera virtualCamera = GameObject.FindObjectOfType<CinemachineVirtualCamera>();
    virtualCamera.Follow = player.transform; // virtual camera의 Follow에 플레이어 등록
}

 

Player.cs

// 불러오기
public void LoadPlayer(SaveData saveData)
{
    if (saveData.playerData == null) return;

    transform.position = saveData.playerData.position;
    transform.eulerAngles = saveData.playerData.rotation;
    currentHealth = saveData.playerData.health;
}

QuestManager.cs

public class QuestManager 
{
    [SerializeField] private Dictionary<int, QuestData> Quests;

    public void Init()
    {
        LoadQuestsFromResources();
        InitializeQuest();
    }

    // Resources 폴더에서 모든 ScriptableObject를 로드하고 Dictionary로 초기화
    private void LoadQuestsFromResources()
    {
        QuestData[] loadedQuests = Resources.LoadAll<QuestData>("Quest");
        Quests = new Dictionary<int, QuestData>();
        if (loadedQuests.Length == 0)
        {
            Debug.LogError("Resources/Quest 폴더에 퀘스트 데이터가 없습니다.");
            return;
        }

        foreach (QuestData questData in loadedQuests)
        {
            if (!Quests.ContainsKey(questData.questId))
            {
                Quests.Add(questData.questId, questData);
                Debug.Log($"퀘스트 Id '{questData.questId}' 인 '{questData.questName}'가 추가되었습니다.");
            }
            else
            {
                Debug.LogWarning($"중복된 questId({questData.questId})가 발견되었습니다. QuestName: {questData.questName}");
            }
        }

        Debug.Log($"{Quests.Count}개의 퀘스트가 로드되었습니다.");
    }

        // Dictionary를 사용하여 퀘스트를 초기화
        private void InitializeQuest()
    {
        if (Quests == null || Quests.Count == 0)
        {
            Debug.LogError("Quests가 null이거나 비어 있습니다. LoadQuestsFromResources를 확인하세요.");
            return;
        }

        foreach (var questData in Quests.Values)
        {
            if (questData.QuestIndex >= Managers.Game.GetPlayer().GetComponent<Player>().index &&
                questData.questState == QuestState.REQUIREMENTS_NOT_MET)
            {
                // 진행 가능한 상태로 변경
                questData.questState = QuestState.CAN_START;
                Debug.Log($"퀘스트 Id '{questData.questId}' 인 '{questData.questName}'가 CAN_START로 초기화되었습니다.");
            }
        }
    }
    
     public void LoadQuestsFromSaveFile(SaveData saveData)
     {
         if (saveData.questData == null) return;

         Quests.Clear();

         foreach (var serializedQuest in saveData.questData)
         {
             QuestData quest = FindQuestByID(serializedQuest.questID);
             if (quest != null)
             {
                 quest.questState = Enum.Parse<QuestState>(serializedQuest.questState);
                 Quests.Add(quest.questId, quest);
             }
         }
     }
     
}

InventoryManager.cs

// 불러오기
public void LoadInventory(SaveData saveData)
{
    if (saveData.inventoryData == null) return;

    inventory.Clear();
    foreach (var serializedSlot in saveData.inventoryData)
    {
        // 저장된 itemID를 통해 ItemData 불러오기
        ItemData itemData = FindItemDataByID(serializedSlot.itemID);
        if (itemData != null)
        {
            // 인벤토리에 새로운 ItemSlot 추가
            inventory.Add(new ItemSlot { itemData = itemData, quantity = serializedSlot.quantity });
        }
        else
        {
            Debug.LogWarning($"ItemData를 찾을 수 없습니다: ID {serializedSlot.itemID}");
        }
    }
}

 

이렇게 해주고 테스트해보면 정상적으로 작동하는것을 볼 수 있다.

 

+ Recent posts