프로젝트 일지/C#

[C#/TIL] TextRPG 팀 프로젝트 일지 | 게임 데이터 Json 파일로 저장&로드하기

톰마토 2025. 2. 12. 23:35
728x90
글 목차
- 팀플 일지
- Json 파일 Save & Load 기능 만들기
  ㄴ 📁게임 데이터를 json 파일로 저장하기
  ㄴ 📁Json으로 된 데이터 파일 로드하기

오늘 한 일

- 팀플 일지

오늘 회의록 일부

개발 남은 할 일들 하면서 나는 게임 데이터 저장을 구현해보게 됐다. json 파일 입출력 꼭 해보고 싶었는데 팀플로 해보게 되니 더 좋았다.

오후에는 발표에 필요한 내용과 자료를 의논해보고, 지금까지의 전체 코드 리뷰를 빠르게 해보았다. 간단하고 부담없이 진행하기 위해 코드 자랑 콘테스트라고 생각하자고 했다. ㅋㅋㅋ

회의록 일부..

 

이후로는 [ 테스트 -> 버그발생 -> 수정 -> 테스트 ] 의 반복이었다. 하다보니 버그 목록이 점점 늘어나서 게임 저장 기능을 추가할 때가 아니라는 판단을 내리고 다같이 버그 테스트와 수정을 반복하게 됐다. 특히 UI 적 요소에 통일감을 주려면 여러 곳에 흩어진 출력 코드를 직접 찾아야 해서 다같이 고생했다. << 디자이너가 필요한 이유..

처음부터 디자인 요소도 맞추면 좋겠구나! 하는 배움도 있었넹.

오늘 오후에만 이만큼 발견하고 해결함

여태 개발에 더 집중하다가 오늘은 테스트만 해서 버그가 많이 몰아서 나온 것 같다!

- Json 파일 Save & Load 기능 만들기

완성도를 위해 게임 저장 기능을 추가 구현하기보다 테스트와 버그 수정에 시간을 더 쏟게 되어서 사라진 기능이지만, 인벤토리 데이터를 저장하고 로드하는 데까지 성공했었다. 프로젝트에 추가하지 않게 된 것은 아쉽지만 경험이 되었기 때문에 블로그에 남겨두려고 한다. 

 

우선 Save함수와 Load함수의 사용 인터페이스를 간편하게 하면서 다른 데이터에 접근이 용이하게 하기 위해 GameManager에 함수만 추가하는 방법과 SaveLoadManager를 만드는 방법 중에 고민했다. 저장하는 게임 데이터들이 다양하다 보니 Save&Load 함수를 하나로 퉁치지 않고 각각 만들어주고 한 번에 다 저장하는 것도 만들기로 결정했다. 그래서 그중 인벤토리 관련 데이터들을 저장하고 로드하는 기능을 맡아 제작했다. 

  ㄴ 📁게임 데이터를 json 파일로 저장하기

구현 시작 전에 잠시 검색하면서 공부(?)해봤는데 진짜 댕놀란점.. 그냥 사용하던 객체를 넣어줘도 직렬화해준다..!? 완전 "딸깍"임!!! 그래서 그냥 InventoryManager.Instance 객체 넣고 바로 돌려봤음. 한글은 깨지지만 이건 옵션만 넣어주면 해결되는 문제고, public 프로퍼티들을 알아서 정리해준다.!! (다른거 해보다가 알았는데 심지어 부모 클래스에 있는 것들도 해줌.)

막 넣어도 데이터.

1. 저장할 데이터 형태 만들기

너무 간편하지만? 필요한 데이터만 저장하기 위해 소지한 아이템 ID, 장착된 아이템 ID, 소지한 포션 종류와 개수만 가공해서 저장하기로 했다.

  • 클래스도 struct도 string 배열도 모두 직렬화가 가능하다. 튜터님께서 특별한 이유가 없다면 보통 클래스로 한다고 하심.
  • 어떤 자료형이든 직렬화가 가능하다. 다만 내부 프로퍼티가 public이어야 한다!
// 저장 데이터의 형태가 될 클래스.
public class InventoryData
{
    public List<int> Inventory { get; set; }
    public List<int> EquipItems { get; set; }
    public List<Dictionary<int, int>> PotionSlot { get; set; }
    public InventoryData()
    {
        Inventory = new List<int>();
        EquipItems = new List<int>();
        PotionSlot = new List<Dictionary<int, int>>();
    }
}

 

발생했던 문제 : data는 있는데 직렬화 결과값에 빈 칸만 나오는 문제.

  • 에러도 아니고 직렬화만 안됐다. 알고보니 JsonSerializer.Serialize로 직렬화할 때는 넣어준 데이터의 public 프로퍼티만 읽어간다. 필드를 못읽는다.
    => InventoryData 클래스의 데이터를 모두 프로퍼티로 바꿔 해결했다.
  • 튜터님께서는 Newtonsoft.Json 사용하시고 그걸 사용할 때는 필드도 됐다고 하셨음. 다른 팀원분도 튜터님께 다른 문제로 질문드렸을 때 Newtonsoft.Json사용해서 해결하라는 말씀을 하셨다고 하심. Newtonsoft의 json이 국룰인가?!!

 

2. 저장하기

저장할 데이터를 직렬화한 뒤 파일에 쓰면 된다. 앞서 말했듯이 "딸깍" 하면 된다.

  • [직렬화] JsonSerializer.Serialize -> [파일쓰기] File.WriteAllText(filePath, json)
/// <summary>
/// 인벤토리 데이터 저장
/// </summary>
public void InventorySave()
{
    string filePath = "../../../DataBase/SaveData/InventoryData.json";
    InventoryManager inventoryManager = InventoryManager.Instance;
    InventoryData data = new InventoryData();
    // 소지 아이템 Id 저장 코드   
    // 장착 아이템 Id 저장 코드
    // 소지 포션 Id와 개수 저장 코드
    try
    {
        string json = JsonSerializer.Serialize(data, new JsonSerializerOptions { WriteIndented = true /*줄바꿈, 들여쓰기 정렬 해줌*/});
        File.WriteAllText(filePath, json);
    }
    catch(Exception ex)
    {
        Console.WriteLine("인벤토리 데이터 저장 오류 {0}", ex.Message);
    }
}

 

그럼 아래와 같은 세이브 데이터 json 파일이 생성된다!

InventoryData.json 예시

  ㄴ 📁Json으로 된 데이터 파일 로드하기

파일 읽어온 후 Deserialize 기능 또 "딸깍" 해주면 된다. 아래 두 줄이 핵심이다.

  • string json = File.ReadAllText(filePath);
  • JsonSerializer.Deserialize<InventoryData>(json);

try문 내부가 역직렬화 코드다. 나는 역직렬화 되어 나온 InventoryData 객체를 InventoryManager에 있는 데이터 적용하는 함수로 바로 보내준 것이다.

        /// <summary>
        /// 인벤토리 데이터 로드
        /// </summary>
        public void InventoryLoad()
        {
            string filePath = "../../../DataBase/SaveData/InventoryData.json";
            if(!File.Exists(filePath))
            {
                // 저장된 데이터 없을 때 처리
                return;
            }
            string json = File.ReadAllText(filePath);
            try
            {
                InventoryManager.Instance.ApplyLoadedData(JsonSerializer.Deserialize<InventoryData>(json));
            }
            catch (Exception ex)
            {
                Console.WriteLine("인벤토리 데이터 로드 오류 {0}", ex.Message);
            }
        }

 

이제 로드할 시점에서 SaveLoadManager.Instance.InventoryLoad() 해주면 게임 실행 시 아이템 목록, 장착된 아이템 정보와 보유 포션 목록이 이전 게임 데이터대로 적용된다.

인벤토리 모습

 

 

728x90