07
26

JsonUtility.ToJson

JsonUtility.ToJson 메서드는 C# 객체를 JSON 형식의 문자열로 직렬화하는 방법이다.

주로 게임 설정, 플레이어 데이터, 게임 상태 등의 정보를 파일로 저장하거나
네트워크 통신을 위해 데이터를 JSON 형식으로 변환하는 데에 사용된다.

JsonUtility.ToJson 메서드는 Unity의 built-in JSON 유틸리티이기 때문에
간단하고 편리하게 사용할 수 있다.

System.Serializable 어트리뷰트를 붙인 변수는 JsonUtility.ToJson을 사용하여 직렬화할 수 있다.
System.Serializable 속성은 Unity에서 제공하는 직렬화 시스템에게 해당 변수를 직렬화할 수 있음을
알려주는 역할을 한다.
따라서 변수에 이 속성을 추가하면 JsonUtility.ToJson을 사용하여
해당 변수와 값을 직렬화할 수 있다.

System.Serializable 어트리뷰트를 붙인 속성은 직렬화 할 수 없다.
JsonUtility는 Unity에서 제공하는 JSON 직렬화 도구로서,
기본적으로는 public 멤버 변수에 대한 직렬화를 지원한다.

그러나 속성은 자동으로 생성되는 getter 및 setter 메서드를 통해 접근하므로 직렬화할 수 없는 제한이 있다.
따라서 JsonUtility.ToJson을 사용하여 속성을 직렬화하려면 대신
해당 속성에 대한 멤버 변수를 선언하고,
그 멤버 변수에 [SerializeField] 속성을 추가하여 직렬화할 수 있도록 해야 한다.

이렇게 하면 JsonUtility.ToJson을 사용하여 해당 멤버 변수와 값을 직렬화할 수 있다.
  • 예제 코드
// 저장할 데이터 클래스 정의
[System.Serializable]
public class PlayerData
{
    public string playerName;
    public int playerScore;
    public bool isAlive;
}

// 데이터를 JSON 형식의 문자열로 직렬화
PlayerData playerData = new PlayerData();
playerData.playerName = "John";
playerData.playerScore = 1000;
playerData.isAlive = true;

string jsonData = JsonUtility.ToJson(playerData);
  • 위 예제 코드에 이어서 파일로 저장하려면 아래 1,2번 중 선택할 수 있다.
1. File.WriteAllText(filePath, jsonData);
지정된 파일에 텍스트를 작성한다.
만약 파일이 이미 존재하는 경우, 해당 파일을 덮어쓴다.
따라서 게임 데이터를 지정된 파일에 저장할 수 있고, 이미 파일이 존재하는 경우도 덮어쓸 수 있다.
덮어 쓰게 되면 기존 파일의 내용은 삭제된다. (타이탄폴2가 이런 저장 방식이었던 것 같다.)
2. File.CreateText(filePath).Write(json);
파일을 생성하고 해당 파일에 텍스트를 작성하기 위한 StreamWriter를 반환하지만,
덮어쓰기를 하기 위한 명령이 없다.
즉, 파일이 이미 존재하는 경우에도 해당 파일을 덮어쓰지 않고, 새로운 파일을 생성한다.
3. new StreamWriter(serializedData).WriteLine();
StreamWriter 생성자는 파일을 생성하는 것이 아니라
텍스트를 쓰기 위한 StreamWriter를 생성하므로 사용 방식이 다르다.

ScriptableObject.CreateInstance

ScriptableObject.CreateInstance 메서드는
ScriptableObject 클래스를 상속받은 커스텀 데이터 클래스의 인스턴스를 생성하는 방법이다.

ScriptableObject은 파일로 저장되지 않고 메모리에만 존재하며,
여러 오브젝트들 사이에서 데이터를 공유하고 관리하는 데에 주로 사용된다.

주로 게임 아이템, 레벨 데이터, 에셋 데이터 등의 정보를 관리하는 데에 활용된다.

일반적으로 게임에서 데이터를 저장하고 불러오기 위해 스크립트 기반의 사용자 정의 데이터 구조를 사용한다.

ScriptableObject.CreateInstance 함수를 사용하여 해당 데이터 구조의 인스턴스를 생성하고,
해당 인스턴스에 필요한 정보를 설정한 다음에 이를 파일이나 플레이어의 기기에 저장할 수도 있다.
  • 예제 코드 1
// ScriptableObject로 저장할 데이터 클래스 정의
[System.Serializable]
public class ItemData : ScriptableObject
{
    public string itemName;
    public int itemValue;
}

// ScriptableObject 인스턴스 생성 및 데이터 설정
ItemData newItem = ScriptableObject.CreateInstance<ItemData>();
newItem.itemName = "Health Potion";
newItem.itemValue = 50;
데이터를 저장하기 위해서는
해당 ScriptableObject 인스턴스의 값을 적절한 형식으로 직렬화하여 저장하면 된다.
이때 파일 시스템을 사용하거나, 데이터베이스를 활용하거나,
플레이어의 기기에 저장할 수 있는 다양한 방법이 있다.

데이터를 불러오기 위해서는 저장된 데이터를 읽어와서 적절한 형식으로 역직렬화하여 ScriptableObject.CreateInstance 함수를 사용하여 인스턴스를 생성한 후,
해당 인스턴스에 데이터를 로드하면 된다.
그리고 게임에서 필요한 곳에서 이 데이터를 활용하여 게임 플레이에 반영할 수 있다.

이런 방식으로 ScriptableObject.CreateInstance 함수를 사용하여
게임의 저장 및 불러오기 기능을 구현할 수 있으며,
게임 데이터의 관리와 유지보수에 유용한 구조를 제공할 수 있다.
  • 예제 코드 2
using UnityEngine;

// 사용자 정의 데이터 구조를 정의하는 ScriptableObject 클래스
[System.Serializable]
public class GameData : ScriptableObject
{
    public string playerName;
    public int playerScore;
}

public class GameManager : MonoBehaviour
{
    public GameData gameData;

    private void SaveGameData()
    {
        // ScriptableObject.CreateInstance 함수를 사용하여 GameData 인스턴스 생성
        GameData savedData = ScriptableObject.CreateInstance<GameData>();

        // 현재 게임 상태를 저장할 데이터 설정
        savedData.playerName = "John";
        savedData.playerScore = 100;

        // 생성된 데이터를 파일이나 플레이어의 기기에 저장
        // 여기에서는 PlayerPrefs를 사용하여 플레이어 기기에 저장하는 예제를 보여줍니다.
        string serializedData = JsonUtility.ToJson(savedData);
        PlayerPrefs.SetString("SavedGameData", serializedData);
        PlayerPrefs.Save();

        // 생성된 데이터를 해제
        Destroy(savedData);
    }

    private void LoadGameData()
    {
        // 저장된 데이터를 파일이나 플레이어의 기기에서 불러오기
        // 여기에서는 PlayerPrefs를 사용하여 플레이어 기기에서 불러오는 예제를 보여줍니다.
        if (PlayerPrefs.HasKey("SavedGameData"))
        {
            string serializedData = PlayerPrefs.GetString("SavedGameData");
            GameData loadedData = JsonUtility.FromJson<GameData>(serializedData);

            // 불러온 데이터를 게임에 적용
            gameData = loadedData;
        }
        else
        {
            // 저장된 데이터가 없을 경우 초기화
            gameData = ScriptableObject.CreateInstance<GameData>();
            gameData.playerName = "Unknown";
            gameData.playerScore = 0;
        }
    }
}

CSV로 저장하고 StreamWriter와 StringBuilder 사용

CSV 형식은 콤마(,)로 구분된 텍스트 파일로 데이터를 저장하는 방법이다.

주로 테이블 형태의 데이터를 저장하고 읽는 데에 사용된다.

StreamWriter와 StringBuilder를 이용하여 파일에 데이터를 쓰고, 필요할 때 읽어들이는 방식으로 사용된다.

CSV 파일은 텍스트 파일이므로 데이터의 크기가 커지면 저장 및 불러오기에 시간이 오래 걸릴 수 있다.

또한 복잡한 데이터 구조를 저장하기 어려울 수 있다.
  • 예제 코드
using System.IO;
using System.Text;

// CSV 파일에 저장할 데이터
string[][] data = new string[3][]
{
    new string[] { "Name", "Score", "Level" },
    new string[] { "John", "1000", "5" },
    new string[] { "Alice", "800", "3" }
};

// 데이터를 CSV 파일로 저장
using (StreamWriter sw = new StreamWriter("data.csv"))
{
    StringBuilder sb = new StringBuilder();
    foreach (string[] rowData in data)
    {
        sb.AppendLine(string.Join(",", rowData));
    }
    sw.Write(sb.ToString());
}

ISerializable 인터페이스를 상속하여 저장

ISerializable 인터페이스를 상속받은 커스텀 데이터 클래스를 정의하고,
이를 이용하여 데이터를 저장하는 방법이다.

이 방법은 BinaryFormatter 등을 사용하여 데이터를 이진 형식으로 직렬화하고 역직렬화하는 방식으로 사용된다.

주로 데이터베이스 저장, 게임 설정, 퀘스트 정보 등을 파일 또는 네트워크로 저장할 때 활용된다.

바이너리 형식이므로 인간이 직접 읽고 수정하기 어렵다. 호환성이 떨어질 수 있다.
  • 예제 코드
using System;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;

// ISerializable을 상속하여 데이터를 저장할 클래스 정의
[Serializable]
public class SaveData : ISerializable
{
    public string playerName;
    public int playerScore;

    public SaveData() { }

    // ISerializable 인터페이스를 구현하는 메서드
    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("PlayerName", playerName);
        info.AddValue("PlayerScore", playerScore);
    }

    // 역직렬화를 위한 생성자
    public SaveData(SerializationInfo info, StreamingContext context)
    {
        playerName = info.GetString("PlayerName");
        playerScore = info.GetInt32("PlayerScore");
    }
}

// 데이터를 이진 형식으로 저장
SaveData playerSaveData = new SaveData();
playerSaveData.playerName = "John";
playerSaveData.playerScore = 1000;

BinaryFormatter formatter = new BinaryFormatter();
using (FileStream fs = new FileStream("save.dat", FileMode.Create))
{
    formatter.Serialize(fs, playerSaveData);
}
COMMENT