I'm FanJae.

[My Turn Based Console RPG] Day 5. 저장 / 불러오기 기능 추가 본문

Projects/My Turn Based Console RPG

[My Turn Based Console RPG] Day 5. 저장 / 불러오기 기능 추가

FanJae 2026. 5. 19. 23:37

1. 시작에 앞서

- 월요일 오전 10시까지 마감 제출이었기 때문에, 일요일에는 프로젝트를 정리할 필요가 있다고 생각했고, 저장 / 불러오기 기능을 추가하고, 프로젝트를 정리하기로 했다.

 

2. 저장 / 불러오기 기능 구현

 

- 이와 같이 게임을 저장해두면, 메인 화면에서 불러오기가 가능하다.

- 불러오기를 통해서 다시 플레이했던 정보를 불러올 수 있다.

- 불러올 때는 기본적으로 플레이어의 스탯 정보와 인벤토리 정보 등을 불러올 수 있게 처리해두었다.

 

① 저장 데이터 정보

namespace MyConsoleMapleRPG.Save
{
    internal class SaveData // 플레이어 저장 정보
    {
        public string PlayerType { get; set; }

        public int Level { get; set; }
        public int Exp { get; set; }

        public int Hp { get; set; }
        public int Mp { get; set; }

        public int Gold { get; set; }

        public int MaxHp { get; set; }
        public int MaxMp { get; set; }

        public int BaseAttack { get; set; }
        public int BaseDefense { get; set; }

        public List<InventorySaveData> InventoryItems { get; set; }
    }
}

- 저장 데이터 정보는 다음과 같이 담긴다.

- 레벨, 경험치, HP, MP, 골드, 최대 체력, Mp, 기본 공격, 방어, 인벤토리 정보 등을 담는다.

 

② 저장 기능

using System.Text.Json;
using MyConsoleMapleRPG.Character;
using MyConsoleMapleRPG.Enums;
using MyConsoleMapleRPG.Save;
using MyConsoleMapleRPG.Items;
using MyConsoleMapleRPG.Items.Inventory;

namespace MyConsoleMapleRPG.Systems
{
    internal class SaveService
    {
        // 게임 데이터 및 세이브 및 로드를 전담하는 서비스 클래스입니다.
        // 플레이어 스탯 및 인벤토리 상태를 JSON 형식의 로컬 파일로 저장하고 복원합니다.

        private const string SaveFilePath = "MySaveFile.json";

        private readonly JsonSerializerOptions options = new()
        {
            WriteIndented = true
        };

        public void Save(Player player) // 저장
        {
            SaveData saveData = CreateSaveData(player);

            string json = JsonSerializer.Serialize(saveData, options);
            File.WriteAllText(SaveFilePath, json); // json 형태로 파일 쓰고 닫음
        }


        // 인게임 플레이어 객체로부터 파일 저장에 필요한 데이터를 뽑아와서 저장한다.
        private SaveData CreateSaveData(Player player) // 저장 데이터 생성
        {
            return new SaveData
            {
                PlayerType = player.JobType.ToString(),

                Level = player.Level,
                Exp = player.Exp,

                Hp = player.Hp,
                MaxHp = player.MaxHp,
                Mp = player.Mp,
                MaxMp = player.MaxMp,

                BaseAttack = player.BaseAttack,
                BaseDefense = player.BaseDefense,

                Gold = player.Gold,
                InventoryItems = CreateInventorySaveData(player)
            };
        }
        // 플레이어의 인벤토리 슬롯 정보를 리스트 형태로 정제 처리
        private List<InventorySaveData> CreateInventorySaveData(Player player)
        {
            List<InventorySaveData> inventoryItems = new();

            foreach (InventorySlot slot in player.Inventory.Slots)
            {
                inventoryItems.Add(new InventorySaveData
                {
                    ItemId = slot.ItemId,
                    Quantity = slot.Count,
                    IsEquipped = slot.IsEquipped
                });
            }

            return inventoryItems;
        }
    }
}

- 기본적으로, 저장할 때는 플레이어의 스탯 데이터를 먼저 넣고 인벤토리 슬롯 정보는 리스트 형태로 정제하여 처리하였다.

 

③ 불러오기 기능

using System.Text.Json;
using MyConsoleMapleRPG.Character;
using MyConsoleMapleRPG.Enums;
using MyConsoleMapleRPG.Save;
using MyConsoleMapleRPG.Items;
using MyConsoleMapleRPG.Items.Inventory;

namespace MyConsoleMapleRPG.Systems
{
    internal class SaveService
    {
        // 게임 데이터 및 세이브 및 로드를 전담하는 서비스 클래스입니다.
        // 플레이어 스탯 및 인벤토리 상태를 JSON 형식의 로컬 파일로 저장하고 복원합니다.

        private const string SaveFilePath = "MySaveFile.json";

        private readonly JsonSerializerOptions options = new()
        {
            WriteIndented = true
        };

        // JSON 파일을 읽어서 다시 역직렬화
        public SaveData? Load()
        {
            // 파일 없으면 null 리턴
            if (!File.Exists(SaveFilePath))
                return null;

            try
            {
                // 파일 전체 읽어옴
                string json = File.ReadAllText(SaveFilePath); // string 형태로 파일 읽고 닫음
                return JsonSerializer.Deserialize<SaveData>(json);
            }
            catch (IOException) // 파일 입출력 자체 실패
            {
                return null;
            }
            catch (UnauthorizedAccessException) // 접근 권한 문제 
            {
                return null;
            }
            catch (JsonException) // 파싱 실패 
            {
                return null;
            }
        }

        public Player? LoadPlayer()
        {
            // 원본 파일 데이터 가져옴.
            SaveData? saveData = Load();

            if (saveData == null)
                return null;

            // 저장된 문자열을 Enum 유형으로 처리
            if (!Enum.TryParse(saveData.PlayerType, ignoreCase: true, out JobType jobType))
                return null;

            // 해당 직업군에 맞는 인스턴스 생성
            Player player = PlayerFactory.Create(jobType);

            // 생성된 플레이어 기본 능력치 데이터 복구(레벨 보정)
            player.RestoreState(saveData.Level,saveData.Exp,saveData.Hp,saveData.MaxHp,saveData.Mp,saveData.MaxMp,saveData.BaseAttack,saveData.BaseDefense,saveData.Gold);

            // 인벤토리 복구
            RestoreInventory(player, saveData.InventoryItems);

            return player;
        }

        // 불러온 세이브 데이터 목록을 바탕으로 인벤토리 슬롯 생성 및 장착 상태 재구성
        private void RestoreInventory(Player player, List<InventorySaveData> inventoryItems)
        {
            foreach (InventorySaveData inventoryItem in inventoryItems)
            {
                // 데이터 검사
                if (!ItemDatabase.TryGet(inventoryItem.ItemId, out ItemData? itemData))
                    continue;

                
                if (itemData == null)
                    continue;

                // 저장 데이터 구조 그대로 강제 복원
                // 슬롯은 기본적으로 미장착 상태로 생성
                InventorySlot slot = player.Inventory.AddSlotForLoad(inventoryItem.ItemId,inventoryItem.Quantity,false);


                // 장착된 슬롯이있었으면 착용처리
                if (inventoryItem.IsEquipped)
                {
                    player.Equipment.ToggleEquip(slot, itemData, player.JobType, out _);
                }
            }
        }
    }
}

- 불러오기 부분에서는 예외 처리를 진행한다. 원본 데이터 파일을 가져와서 해당 직업군에 맞는 인스턴스를 생성하고, 생성된 플레이어에 대한 기본 능력치 데이터를 복구해준다. (레벨업에 의한 스탯 보정을 하는 작업이다.)

- 이후, 인벤토리 복구를 진행하는데, 불러온 세이브 데이터 목록을 바탕으로 인벤토리와 슬롯의 장착 상태를 재구성한다.

- 기본적으로 슬롯에 모든 정보는 미장착 상태로 생성하되, 장착된 슬롯이 있으면 착용 처리하도록 바꾸었다.

 

 

 
Comments