4-7. 2조 직렬화 (Serialization)

keubung·2024년 11월 4일

직렬화 (Serialization)

- 직렬화란?
오브젝트 데이터를 저장하거나 전송할 수 있도록 데이터를 연속적인 형식으로 변환하는 과정.

  • 게임을 중지하고 다시 시작해도 데이터가 유지되도록 데이터를 직렬화시켜서 파일로 저장한다.
  1. PlayerPrefs

    • PlayerPrefs 활용

예시 코드

  • 데이터를 nickname + ',' + lv + ',' + hp 형식으로 문자열로 변환하여 저장하고, 구분자로 콤마(,)를 사용
  • PlayerPrefs.SetString("PlayerData", SaveData); -> PlayerData라는 키에 데이터를 저장
  • PlayerPrefs.GetString("PlayerData") -> 저장된 데이터 로드
  • 데이터를 콤마(,)를 기준으로 분리하여 datas 배열에 저장
  • 간단한 게임 데이터를 저장하는 데 자주 사용되는 Unity의 내장 시스템
  • 보안에 취약하므로 중요한 데이터는 보관하지 않는 것이 좋다.
public class Player : MonoBehaviour
{
    public string nickname;
    public int lv;
    public float hp;

    public void Save()
    {
        string SaveData = nickname + ',' + lv + ',' + hp;
        PlayerPrefs.SetString("PlayerData", SaveData);
        Debug.Log("저장완료");
    }

    public void Load()
    {
        string loadData = PlayerPrefs.GetString("PlayerData");

        string[] datas = loadData.Split(',');

        this.nickname = datas[0];
        this.lv = int.Parse(datas[1]);
        this.hp = float.Parse(datas[2]);
    }
}
  1. CSV
  • CSV 활용

예시 코드

  • Dictionary<string, string> 타입으로, 각 대사의 키를 문자열 형식(예: "3_3")으로 저장하고, 이에 대응하는 값을 저장
  • Resources.Load("CSVData")를 통해 Resources 폴더 내의 CSVData라는 파일을 불러온다. 파일의 내용은 TextAsset 객체로 불러와 csvData.text를 통해 문자열로 접근
  • Deserialization(data);을 호출하여 CSV 데이터를 파싱하고, 각 대사를 Dictionary에 저장
  • rowData 배열에 행별 데이터 저장. 첫 번째 행(0번 인덱스)은 헤더이므로 1부터 시작하여 반복
  • 각 행의 데이터를 쉼표(,)를 기준으로 분리하여 data 배열에 저장. 첫 번째 요소는 키, 두 번째 요소는 값
  • chatData[data[0]] = data[1];을 통해 각 키와 그에 맞는 값을 Dictionary에 추가
public class Dialogue : MonoBehaviour
{
    private Dictionary<string, string> chatData = new Dictionary<string, string>();

    private void Start()
    {
        TextAsset csvData = Resources.Load<TextAsset>("CSVData");
        var data = csvData.text.TrimEnd();

        Deserialization(data);

        foreach (var item in chatData)
        {
            Debug.Log(item);
        }

        Debug.Log(Show(3,3));
    }

    public void Deserialization(string originData)
    {
        var rowData = originData.Split('\n');

        for (int i = 1; i < rowData.Length; i++)
        {
            var data = rowData[i].Split(',');

            chatData[data[0]] = data[1];
        }
    }

    public string Show(int chapter, int phase)
    {
        string t = $"{chapter}_{phase}";
        return chatData[t];
    }
}
  1. XML
  • XML 활용

예시 코드

  • 루트 노드 GameData와 자식 노드 PlayerData를 생성하고, nickname, lv, hp의 값들을 각각 XML 요소로 추가
  • AssetDatabase.Refresh();는 Unity 에디터에서 리소스를 갱신할 때 사용되므로 에디터 환경에서만 유효하다.
  • Resources.Load("GameDatas") -> Resources 폴더에서 GameDatas.xml 파일을 불러온다.
  • 파일 크기가 큰 데이터를 저장할 때 비효율적

    GameDatas.xml
    
    <GameData>
     <PlayerData>
      <nickname>스파르타</nickname>
      <lv>10</lv>
      <hp>100</hp>
     </PlayerData>
    </GameData>

public class XMLTest : MonoBehaviour
{
public Player player;

private void Start()
{
    //CreateXML(player);
    LoadXML();
}

void CreateXML(Player p)
{
XmlDocument xmlDoc = new XmlDocument();

    //xmlDoc.AppendChild(xmlDoc.CreateXmlDeclaration());

    // 루트 노드
    XmlNode root = xmlDoc.CreateNode(XmlNodeType.Element, "GameData", string.Empty);
    xmlDoc.AppendChild(root);

    // 자식 노드
    XmlNode child = xmlDoc.CreateNode(XmlNodeType.Element, "PlayerData", string.Empty);
    root.AppendChild(child);

    XmlElement nickname = xmlDoc.CreateElement("nickname");
    nickname.InnerText = p.nickname;
    child.AppendChild(nickname);

    XmlElement lv = xmlDoc.CreateElement("lv");
    lv.InnerText = p.lv.ToString();
    child.AppendChild(lv);

    XmlElement hp = xmlDoc.CreateElement("hp");
    hp.InnerText = p.hp.ToString();
    child.AppendChild(hp);

    xmlDoc.Save("./Assets/Resources/GameDatas.xml");

    AssetDatabase.Refresh(); // 에디터에서만 써야함.
}

void LoadXML()
{
    var t = Resources.Load<TextAsset>("GameDatas");

    XmlDocument xmlDoc = new XmlDocument();
    xmlDoc.LoadXml(t.text);

    XmlNodeList nodes = xmlDoc.SelectNodes("GameData/PlayerData");

    XmlNode playerData = nodes[0];

    player.nickname = playerData.SelectSingleNode("nickname").InnerText;
    player.lv = int.Parse(playerData.SelectSingleNode("lv").InnerText);
    player.hp = float.Parse(playerData.SelectSingleNode("hp").InnerText);
}

}

4.JSON

  • JSON 활용

    예시 코드

    • JsonUtility.ToJson(userData)를 사용해 userData 객체를 JSON 형식 문자열로 변환

    • File.WriteAllText로 쓰기, File.ReadAllText로 읽기

    • JsonUtility.FromJson(loadData)를 사용해 JSON 문자열을 UserData 객체로 변환

    • [System.Serializable]를 사용해 JSON으로 직렬화

      public class Character : MonoBehaviour
      {
        public UserData userData;
      
        private void Start()
        {
            Load();
        }
      
        public void Save()
        {
            var saveData = JsonUtility.ToJson(userData);
      
            Debug.Log(saveData);
      
            Debug.Log(Application.persistentDataPath + "/UserData.txt");
            File.WriteAllText(Application.persistentDataPath + "/UserData.txt", saveData);
        }
      
        public void Load()
        {
            string loadData = File.ReadAllText(Application.persistentDataPath + "/UserData.txt");
      
            userData = JsonUtility.FromJson<UserData>(loadData);
        }
      }
      

    [System.Serializable]
    public class UserData
    {
    public string nickname;
    public int lv;
    public float hp;

    public List<string> friends = new List<string>();
    
    public Inventory inven;

    }

    [System.Serializable]
    public class Inventory
    {
    public int size;
    public List items = new List();
    }

    [System.Serializable]
    public class Item
    {
    public int itemId;
    }

  1. SO (Scriptale Object)
  • SO (Scriptale Object) 활용

    예시 코드

    • CreateAssetMenu -> Unity 에디터의 우클릭 메뉴에 ScriptableObject를 생성할 수 있는 옵션을 추가

    • ScriptableObject를 상속

    • 재사용이 빈번한 데이터 구조에 적합

      [CreateAssetMenu(fileName = "SOData", menuName = "SO/Data", order = 1)]
      public class SOData :ScriptableObject
      {
        // Monster
        public string nickname;
        public float maxHp;
        public float maxMoney;
      
        public Rigidbody rb;
        public Collider co;
      }
  • 단순하게 저장했을 경우 보안상으로 좋지 않기 때문에 암호화, 복호화 과정을 거지는 것이 좋다.
    직렬화 -> 암호화 -> 저장 -> 불러오기 -> 복호화 -> 역직렬화 -> 사용
profile
김나영(Unity_6기)

0개의 댓글