직렬화
객체를 바이트 배열로 바꾸어 저장하는것
프로그램의 기반
유니티는 직렬화 데이터들의 집합 이라고 할 수 있음
- Scene,SO, meta, prefab 이 모두 직렬화 데이터
- 메모장을 통해 prefab 등의 파일을 열어볼경우, YAML 형태로 직렬화 되어있는것을 확인할 수 있음
역 직렬화
저장한 데이터를 바이트 배열으로 바꾸어, 다시 객체로 변환하는것
직렬화, 역직렬화의 과정
- 코드들이 메모리에 산재됨
- 재시작시 사용되는 메모리의 위치가 변경, 저장된 메모리는 이전 위치에 유지됨
- 흩어져있는 내용을 정리해서 나열 → 직렬화
- 나열된 값을 원래 자리에 되돌림 → 역직렬화
직렬화를 한 뒤, 나중에 역직렬화가 가능함
→ 직렬화를 통해 저장/통신 이 가능함
초기 직렬화
0, 1 으로 데이터를 나열하여 사용 (바이트 배열)
→ 컴퓨터는 이해하지만, 사람은 이해하기 어려움(개발이 어려움)
→ 사람이 읽을 수 있는 방식으로 직렬화
CSV
- Comma Separated Value
- 컴마(,) 로 값을 나누어서 저장하는 방식
- 엑셀에서 기본 지원
사용법
- 엑셀에서 CSV UTF-8 으로 파일 저장
- 유니티에 넣어서 저장된 값 사용 가능
- ',' 와 엔터로 구분됨
역 직렬화
- CSV 파일을 Resources 폴더에 저장
- CSV 파일을 TextAsset 형태로 로드
- TextAsset을 string 값으로 변환 (변수명.text.TrimEnd())
- TrimEnd 를 하는 이유 : 끝부분에 \n이 존재하며, 이 부분이 공백으로 저장되어있기 때문에 마지막의 공백을 삭제해줄 필요가 있음
- string 값으로 변환된 TextAsset을 '\n' 으로 나누기 (.Split('\n'))
- \n 으로 나눠진 값을 다시 ',' 으로 나누기 (.Split(','))
public class CSVTest : MonoBehaviour
{
private Dictionary<string, string> itemData = new Dictionary<string, string>();
private void Start()
{
TextAsset csvData = Resources.Load<TextAsset>("TestCSV");
var data = csvData.text.TrimEnd();
DeSerialization(data);
foreach(var item in itemData)
{
Debug.Log($"{item.Key} : {item.Value}");
}
}
private void DeSerialization(string originData)
{
var rowData = originData.Split('\n');
for(int i = 0; i < rowData.Length; i++)
{
var data = rowData[i].Split(',');
itemData[data[0]] = data[1];
}
}
}
단점
- 저장된 값이, 어떤 값인지 알기 어려움
XML, json, YAML
Key : Value 값으로 직렬화 되어있어 어떤값인지 알아보기 쉬움
XML
Extensible Markup Language
<루트노드>
<자식노드>
<요소> 요소내용 </요소>
</자식노드>
</루트노드>
이러한 형태로 구성됨
사용
쓰기 (직렬화)
- XmlDocument 를 통해 작성 가능
- XmlDocument 객체를 생성한 뒤, 노드를 만들어 연결해주는 형태
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(); // 에디터에서만 써야함.
}
root 가 루트 노드가 된 뒤, child가 root에 연결되고, child에 nickname, lv, hp 를 연결한 형태로 Xml 이 만들어짐
읽기 (역직렬화)
- Resources 폴더에 Xml 파일을 넣어서 사용
- XmlDocument 클래스의 객체를 선언한 뒤, Resources.Load<TextAsset> 으로 읽어들인 Xml 파일을 객체에 지정
- 지정한 객체의 노드를 선택하여 값을 읽어들일 수 있음
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);
}
https://docs.unity3d.com/kr/2019.4/Manual/JSONSerialization.html
단점
- 규칙이 엄격함
- 노드와 요소를 만들때, 항상 쌍으로 존재하며 열었으면 닫아야함
json
- 사실상 기본값
- 편하게 사용하기위한 관련기능이 많음
- Newtonsoft, LitJson, JsonUtility 등이 있지만, 유니티에선 JsonUtility를 사용 (성능이 가장 좋음)
- JsonUtility 의 단점 : 확장성이 좋지 않음
직렬화 규칙
- public/ SerializeField 속성이 있어야함
- static이 아니여야함
커스텀 직렬화
지원하지 않는 항목을 직렬화할 때 사용
사용
쓰기 (직렬화)
public class Character : MonoBehaviour
{
public UserData userData;
public void Save()
{
var saveData = JsonUtility.ToJson(userData);
Debug.Log(saveData);
Debug.Log(Application.persistentDataPath + "/UserData.txt");
//저장 - string 값으로 변환하여 저장
File.WriteAllText(Application.persistentDataPath + "/UserData.txt", saveData);
}
}
[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<Item> items = new List<Item>();
}
[System.Serializable]
public class Item
{
public int itemId;
}
읽기 (역직렬화)
public void Load()
{
string loadData = File.ReadAllText(Application.persistentDataPath + "/UserData.txt");
userData = JsonUtility.FromJson<UserData>(loadData);
}
※Application.persistentDataPath : 환경에 적합한 경로를 지정
주의사항
- json 을 사용하여 데이터 저장시, json 파일에 접근이 가능하면 저장한 데이터를 변조 할 수 있음
- 서버에 데이터를 저장하지 않을경우, 암호화를 하여 데이터 변조를 막아줄 필요가 있음
- AES 등을 사용하여 직렬화시 암호화를 해줄 필요가 있음
스크립터블 오브젝트 SO
- 저장할 내용을 SO 오브젝트를 만들어서 저장
특징, 문제점
- json, Xml 등에서 저장할 수 없는 내용을 저장할 수 있음 (Unity 컴포넌트 정보를 저장할 수 있음)
- 에디터 내에서는 저장에 문제가 없지만, 빌드를 할경우 재시작시 빌드했던 수치로 값이 초기화됨
- 많은 게임오브젝트가 같은 내용을 사용하더라도 하나의 SO를 사용하기 때문에 최적화에 좋음
※ 변하지 않는 값을 SO를 사용하여 저장, 사용
'내일배움캠프 > TIL' 카테고리의 다른 글
유니티 팀프로젝트 숙련주차 - 발표 시연영상 (0) | 2024.11.06 |
---|---|
유니티 숙련주차 팀프로젝트 - 병합 완료 (0) | 2024.11.05 |
유니티 숙련주차 팀 프로젝트 - 제작 시스템 추가 (0) | 2024.11.01 |
팀프로젝트 제작 시작 - 크래프팅 시스템 (0) | 2024.10.31 |
2차 디자인패턴 특강 정리 - 이벤트기반 프로그래밍, 명령패턴 (0) | 2024.10.30 |