오늘은 팀프로젝트에서 만든 저장 및 불러오기 시스템을 설명하며,

나중에 또 좋은 틀로 사용할 수 있을것 같아서 작성하게 되었다.

 

3가지 슬롯을 만들고 저장 및 불러오기 그리고 저장 된 파일이 없을면 새로운 시작 을 표기하고 싶었고

 

저장된 슬롯의 파일을 지우는 기능도 만들고싶었다.(욕심이 그득)

 

우선 코드를 보여주고 설명을 하겠다.

 

public class SaveSlotUI : MonoBehaviour
{
    public Button[] slotButtons;
    public TextMeshProUGUI[] slotTexts;
    public SaveLoadManager saveLoadManager;
    public TMP_InputField nameInputField;
    public GameObject nameEntryPanel;

    private int selectedSlot = -1;

    private void Start()
    {
        LoadSlotInfo();
    }

    private void LoadSlotInfo()
    {
        for (int i = 0; i < slotButtons.Length; i++)
        {
            var saveData = saveLoadManager.LoadGame(i);
            slotTexts[i].text = saveData != null ? $"이름 : {saveData.playerName} \n 레벨 : {saveData.level}" : "새로운 시작";
        }
    }

    public void SelectSlot(int slot)
    {
        AudioManager.Instance.PlaySFX(AudioManager.Sfx.Button);
        selectedSlot = slot;

        var saveData = saveLoadManager.LoadGame(slot);

        if (saveData != null)
        {
            saveLoadManager.SetCurrentSlotIndex(slot);
            StartSavedGame(saveData);
        }
        else
        {
            nameEntryPanel.SetActive(true);
            saveLoadManager.SetCurrentSlotIndex(slot);
        }
    }


    public void StartNewGame()
    {
        AudioManager.Instance.PlaySFX(AudioManager.Sfx.Button);
        if (selectedSlot < 0) return;

        string playerName = nameInputField.text;
        if (string.IsNullOrEmpty(playerName)) return;

        SaveData newSaveData = new SaveData
        {
            playerName = playerName,
            level = 1,
        };

        saveLoadManager.SaveGame(newSaveData, selectedSlot);

        nameEntryPanel.SetActive(false);
        LoadSlotInfo();

        LevelController.Instance.currentLevel = 1;
        SceneManager.LoadScene("Level1");
        PuzzleManager.Instance.InitializePuzzle();
    }

    public void DeleteSlot(int slot)
    {
        AudioManager.Instance.PlaySFX(AudioManager.Sfx.Button);
        saveLoadManager.DeleteGame(slot);
        LoadSlotInfo();
    }

    private void StartSavedGame(SaveData saveData)
    {
        LevelController.Instance.currentLevel = saveData.level;
        SceneManager.LoadScene($"Level{saveData.level}");
        PuzzleManager.Instance.InitializePuzzle();
    }
}

이 SaveSlotUI의 역할은 플레이어가 게임을 저장하거나 불러오는 세이브 슬롯 UI를 관리한다.

 

주요메서드는 아래와 같다.

 

LoadSlotInfo : 각 슬롯의 저장된 데이터를 불러와 UI에 표시하고, 저장된 정보가 없으면 "새로운 게임"을 표시한다.

SelectSlot : 슬롯을 선택했을 떄 햇당 슬롯에 데이터가 있으면 게임을 시작, 아니면 이름 입력 패널을 표시한다.

StartNewGame : 새로운 게임을 시작할때 호출되고 초기데이터를 저장한다.

DeleteSlot : 해당 슬롯의 데이터를 삭제한다

StartSavedGame : 저장된 게임을 불러와서 해당 레벨로 시작한다.

 

다음은 세이브 로드 매니저이다.

using System.IO;
using UnityEngine;

public class SaveLoadManager : Singleton<SaveLoadManager>
{
    private string saveFolderPath = @"C:\PuzzleHorror\Saves";
    public int currentSlotIndex = 0;

    private void Start()
    {
        if (!Directory.Exists(saveFolderPath))
        {
            Directory.CreateDirectory(saveFolderPath);
        }
    }

    public void SaveGame(SaveData data, int slotIndex)
    {
        string filePath = Path.Combine(saveFolderPath, $"save_slot_{slotIndex}.json");
        string jsonData = JsonUtility.ToJson(data, true);
        File.WriteAllText(filePath, jsonData);
    }

    public void SaveCurrentProgress(int level)
    {
        SaveData currentData = LoadGame(currentSlotIndex);

        string playerName = currentData.playerName;

        SaveData data = new SaveData
        {
            playerName = playerName,
            level = level
        };

        SaveGame(data, currentSlotIndex);
    }

    public SaveData LoadGame(int slotIndex)
    {
        string filePath = Path.Combine(saveFolderPath, $"save_slot_{slotIndex}.json");
        if (File.Exists(filePath))
        {
            string jsonData = File.ReadAllText(filePath);
            return JsonUtility.FromJson<SaveData>(jsonData);
        }
        return null;
    }

    public void DeleteGame(int slotIndex)
    {
        string filePath = Path.Combine(saveFolderPath, $"save_slot_{slotIndex}.json");
        if (File.Exists(filePath))
        {
            File.Delete(filePath);
        }
    }

    public void SetCurrentSlotIndex(int slotIndex)
    {
        currentSlotIndex = slotIndex;
    }
}

 

오히려 UI보다 간단해보이는 진짜 세이브 로드를 담당하는 세이브 로드 매니저이다.

 

위와 동일하게 주요 메서드만 설명하겠다

 

SaveGame : 특정 슬롯의 SaveData를 JSON 파일로 저장합니다.

SaveCurrentProgress : 현재 슬롯에 진행 중인 레벨 정보를 업데이트합니다.

LoadGame : 특정 슬롯에서 JSON 파일을 읽어 SaveData 객체로 변환하여 불러옵니다.

DeleteGame : 특정 슬롯의 저장 파일을 삭제합니다.

SetCurrentSlotIndex : 현재 선택된 슬롯의 인덱스를 설정합니다. (슬롯이 3개라서 UI에서 받아옴)

 

그리고 SaveData는 이렇게 간단하게 구성되어있습니다.

 

[Serializable]
public class SaveData
{
    public string playerName;
    public int level;
}

 

 

 

[Serializable]의 특성을 이용하여, JSON형태로 직렬화 및 역직렬화가 가능합니다.