내일배움캠프 10일차 [TIL]

장민제·2025년 4월 18일

내일배움캠프

목록 보기
10/41
post-thumbnail

✅ 오늘의 요약

  • Text Rpg 데이터 저장기능까지 구현 완료했다.
    예상과는 다르게 쉽게 흘러가주지 않아서 당황했지만 그래도 결국은 이겨냈다.
  • 정렬(Sorting) 알고리즘에 대해 공부하고 정리했다.

📃 데이터 저장/불러오기 기능 구현

  • 플레이어의 진행 정보를 저장하고,
    다시 게임을 실행했을 때 이어서 플레이 할 수 있게 저장/불러오기 기능을 구현해보았다.
  • json 형식의 파일로 직렬화 하여 저장하는 방식을 택했다.

🔧 초기 시도

PlayerSaveData를 클래스로 만들어, 저장할 데이터를 그대로 멤버 변수로 선언해주었다.

[Serializable]
public class PlayerSaveData
{
    public string Name;
    public JobType Job;
    public Stats Stats;
    public List<Item> InventoryItems;
}

그런 후 SavePlayer()를 통해 JSON으로 직렬화 후 저장하는 메서드를 구현했다.

public static void SavePlayer(Player player, Action onCompleted)
{
	// 플레이어의 데이터 PlayerSaveData로 저장
    PlayerSaveData data = new PlayerSaveData
    {
        Name = player.Name,
        Job = player.GetJobType(),
        Stats = player.stats,
        InventoryItems = player.inven.Items.ToList()
    };
	
    // JSON 형식으로 직렬화 후 파일에 저장
    string json = JsonSerializer.Serialize(data, new JsonSerializerOptions { WriteIndented = true });
    File.WriteAllText(PLAYER_SAVEDATA, json);
	
    // 완료 콜백 호출
    onCompleted?.Invoke();
}

⛔ 하지만 여기서 문제가 발생했다.
PlayerData.json 파일이 생기긴 했지만 안에 내용이 없는 것이었다.
그런데 비슷한 방식으로 아이템 저장도 구현을 했었는데 아이템은 또 저장이 잘되었다.

public struct ItemSaveData // 아이템 저장 데이터 구조체
{
    // 아이템의 정보들
    public string Name { get; set; }
    public ItemType Type { get; set; }
    public int Value { get; set; }
    public string Info { get; set; }
    public int Cost { get; set; }
    public bool IsOwn { get; set; }
    public bool IsEquip { get; set; }

    public ItemSaveData(string name, ItemType type, int value, string info, int cost, bool isOwn, bool isEquip)
    {
        Name = name;
        Type = type;
        Value = value;
        Info = info;
        Cost = cost;
        IsOwn = isOwn;
        IsEquip = isEquip;
    }
}

ItemSaveData는 구조체로 만들어 저장하고

// Item SaveData로 저장
public ItemSaveData ToSaveData()
{
    // 각 아이템 정보들 ItemSaveData에 저장하여 반환
    return new ItemSaveData(
        itemStat.Name,
        Type,
        itemStat.Value,
        itemStat.Info,
        itemStat.Cost,
        IsOwn,
        IsEquip
    );
}

Item 내부 변수들이 다 외부에서 접근이 불가능하도록 해놓아서 클래스 내부에서 데이터를 저장해서 넘기도록 하였다.

아무튼 이 아이는 잘 저장이 되는데 왜 Player의 정보는 잘 안되는가 해서 찾아보니 저장하려는 정보에 private 접근자가 있으면 직렬화가 되지않는다는 정보를 알게되었다.

그래서 Stats 안에 있는 변수를 다 뽑아냈다.

public class PlayerSaveData
{
    public string Name;
    public JobType Job;
    public int Level;
    public int Hp;
    public float Attack;
    public int Defense;
    public int ItemAttack;
    public int ItemDefense;
    public int Gold;
    public int clearTime;
}

이렇게 정리하고 Player 클래스에도 Item과 같이 ToSaveData() 메서드를 구현했다.

// 플레이어의 정보들 PlayerSaveData로 변환
public PlayerSaveData ToSaveData()
{
    PlayerSaveData data = new PlayerSaveData
    {
        Name = Name,
        Job = jobType,
        Level = stats.Level,
        Hp = stats.Hp,
        Attack = stats.Attack,
        Defense = stats.Defense,
        ItemAttack = stats.ItemAttack,
        ItemDefense = stats.ItemDefense,
        Gold = stats.Gold,
        clearTime = clearTime,
    };

    // 저장된 데이터 반환
    return data;
}

아니 그런데 이게 무슨 일인가 여전히 내용이 저장이 되지않는 것이었다.
그래서 ItemSaveData과 똑같이 구조체로 만들고 프로퍼티로 다 만들어주었다

[Serializable]
public struct PlayerSaveData
{
    public string Name { get; set; }
    public JobType Job { get; set; }
    public int Level { get; set; }
    public int Hp { get; set; }
    public float Attack { get; set; }
    public int Defense { get; set; }
    public int ItemAttack { get; set; }
    public int ItemDefense { get; set; }
    public int Gold { get; set; }
    public int clearTime { get; set; }
}

이렇게 하니 잘 저장이 되는 모습을 확인할 수 있었다. 😁

그래서 왜 그랬는지 여기저기 찾아보니,

  • Json은 프로퍼티를 더 선호
  • 값 타입이 참조 타입보다 직렬화가 안정적

이라는 내용을 찾아 볼 수 있었다.
이제 알게되었으니 앞으로는 데이터를 저장할 때 위 사항들을 유념해서 저장을 시도해야겠다 👌

그 이후로 불러오는 것은 어려울게 없으니,
JSON 파일 역직렬화해서 데이터로 가져오고, 그 값들을 다 적용시켜주었다.
추가로 데이터를 저장도 했으니 이제 게임 시작 시 새로하기이어하기 화면도 만들어주었다.

public void StartGame()
{
    while (true)
    {
        Console.Clear();
        BannerManager.Show("SPARTA");

        int choice = InputHandler.InputValidator("1. 새로하기\n2. 이어하기", 1, 2);

        if (choice == 1) // 새로 하기
        {
            ItemManager.Instance.items = ItemManager.Instance.CreateItem();
            CreatePlayer();
            break;
        }

        else    // 이어 하기
        {
            player = SaveData.LoadPlayer();

            if (player != null)
            {
                // 저장된 아이템 로드
                ItemManager.Instance.LoadItems(player);
                Console.WriteLine("게임 시작!");
                MainGame();
                break;
            }
        }
    }
}

// 저장하고 나가기
private void SaveAndQuit()
{
    // 플레이어와 아이템 정보 저장
    SaveData.SavePlayer(player, () => Console.WriteLine("\n플레이어 정보 저장 완료!"));
    ItemManager.Instance.SaveItems(() => Console.WriteLine("\n아이템 데이터 저장 완료!"));

    Console.WriteLine("\n게임이 종료됩니다...");
    Thread.Sleep(1000);
}

저장 완료시 콜백 호출받아 Console에 출력되도록 하였다.

💻 실제 화면

과제 제출 전에 급하게 부랴부랴 구현해서 코드도 지저분하고, 로컬에 저장되지만 암호화도 되지않아 데이터의 조작이 너무나도 쉬운게 흠이다.


정렬(Sorting) 알고리즘 공부

  • 퀵 정렬, 분할 정렬 같은 경우에는 재귀적으로 호출되는 방법들이라 처음에 데이터의 흐름을 따라가는데 조금 어려웠지만 그래도 차근차근 생각하며 진행해보니 이해가 어느정도 되었다.
  • C#에는 LINQ라는 훌륭한 기능이 있기때문에 적당한 크기의 데이터 선에서는 아마 충분하지 않을까 생각이 든다. (그러나 너무 어렵다.)
  • 그래도 알고리즘의 개념을 알고 있는것은 아주 중요하다고 생각한다.

정렬(Sorting) 알고리즘


🧠 느낀점

TextRpg를 만들어보면서 문법과 많이 친해졌다고 느낀다.
이제 알고리즘 공부도하고 관련 코딩테스트 문제들도 많이 풀어보며
알고리즘들의 개념문제 해결능력을 길러 코딩테스트에서도 좋은 평가를 받을 수 있도록 노력할 것이다.

profile
Unity, C#

0개의 댓글