직렬화(Serialization)란?
직렬화에 대한 개념은 다음의 링크를 참조하도록 하자.
- https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/concepts/serialization/
- https://docs.unity3d.com/kr/530/Manual/script-Serialization.html
여기서 사용할 유니티 에디터의 시리얼라이저는 실시간 게임환경에서 실행되기 때문에 다른 프로그래밍 환경의 시리얼라이저와는 다르게 동작한다.
클래스 내의 필드가 직렬화되도록 하려면 다음의 조건을 만족해야한다.
- public이거나 [SerializeField] 속성이 있어야한다.
- static이 아니어야한다.
- const가 아니어야한다.
- readonly가 아니어야한다.
- 직렬화가 가능한 필드타입이어야한다. (단순 필드 타입만 가능하다.)
- [Serializable] 속성이 있는 비 추상, 일반 클래스
- [Serializable] 속성이 있는 커스텀 구조체
- UnityEngine.Object에서 파생된 오브젝트
- 프리미티브 데이터 타입(int, float, double, bool. string 등)
- 열거형 타입
- 특정 Unity 타입 : Vector2, Vector3, Vector4, Rect, Quaternion, Matrix4x4, Color, Color32, LayerMask, AnimationCurve, Gradient, RectOffset, GUIStyle
추가적으로, 단순 필드 타입의 배열과 List<T>도 직렬화가 가능하다.
그러나 다단계 타입(다차원 배열, 가변 배열, 중첩 컨테이너 타입)의 직렬화를 지원하지는 않는다.
아예 불가능하지는 않다. 중첩 타입을 클래스 또는 구조체 랩핑, ISerializatinCallbackReceiver콜백을 사용하여 커스텀 직렬화를 수행한다면 가능하다.
( 참고 : https://docs.unity3d.com/kr/current/Manual/script-Serialization-Custom.html )
직렬화 - 세이브, 로드
데이터를 직렬화, 역직렬화하여 파일에 담는 순서를 살펴보자.
- 세이브
- 프로그램에서 사용할 변수 선언 및 할당.
- 직렬화용 Serializable클래스 선언.
- FileStream 클래스를 이용하여 파일 생성.
- Serializable클래스에 프로그램에서 사용하는 변수 값 담기. (1->2)
- BinaryFormatter 클래스를 이용하여 4를 직렬화 -> 3의 파일에 담기.
- 생성된 파일 닫기.
- 로드
- FileStream 클래스를 이용하여 파일 열기.
- 불러온 데이터를 BinaryFormatter 클래스를 이용해 역직렬화.
- 역직렬화한 데이터를 Serializable 클래스에 담기.
- Serializable 클래스에 담긴 데이터를 실제 프로그램에서 쓰는 변수에 재 할당.
- 프로그램 사용.
위의 순서를 보면 Serializable 클래스가 데이터 직렬화, 역직렬화할 때 사용하는 일종의 임시 클래스로 사용된다는 것을 볼 수 있다.
실제 데이터는 일반 클래스를 사용하고, 저장하고자 하는 데이터만 Serializable 클래스에 담아 파일 세이브 및 로드를 진행한다.
다음은 직렬화 및 역직렬화를 진행할 수 있는 코드다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | public class SuperSerialization<T> { private string ext = ".dat"; private BinaryFormatter bf; private FileStream file; public void Serialization(T tempData, string filename, string path) { bf = new BinaryFormatter(); file = File.Create(path + "/" + filename + ext); bf.Serialize(file, tempData); file.Close(); } public void DeSerialization(string filename, string path, out T deserialized) { bf = new BinaryFormatter(); file = File.Open(path + "/" + filename + ext, FileMode.Open); if (file != null && file.Length > 0) { deserialized = (T)bf.Deserialize(file); } else { deserialized = default(T); } file.Close(); } } | cs |
[참조 1]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | [Serializable] public class TempSerializeData { public string uid; public string username; } public class CSerialization: SuperSerialization<TempSerializeData> { private TempSerializeData tempData; private string filename = "test"; public void Serialization(string path) { if (tempData == null) { DebugConsole.Log("tempData에 값이 채워지지 않음."); return; } Serialization(tempData, filename, path); } public void DeSerialization(string path, out object[] data) { TempSerializeData output; DeSerialization(filename, path, out output); if (output == default(TempSerializeData)) { data = default(object[]); DebugConsole.Log("불러오는 데 오류 발생"); return; } data = new object[2]; data[0] = output.uid; data[1] = output.username; } public void SetArguments(string uid, string username) { if (tempData == null) { tempData = new TempSerializeData(); } tempData.uid = uid; tempData.username = username; } } | cs |
[참조 2]
참조1 (SuperSerialization)은 파일 이름, 경로, Serializable객체만 받아서 직렬화, 역직렬화만 진행한다.
수시로 바뀔 수 있는 파일 이름, 경로, 객체는 참조2에 있는 클래스(CSerialization)에서 처리한다.
예외처리
- 파일에 저장하던 데이터의 필드명이 바뀌는 경우?
기존 값은 계속 사용하지만, 그 값에 대한 변수 명이 바뀌게 되면 역직렬화할 때 값을 찾을 수 없다.
이 경우를 대비해 유니티에서는 [FormerlySerializedAs] 키워드를 지원한다.
바뀐 필드 명 위에 해당 키워드를 선언하여 이전에 사용하던 필드명을 등록해 놓으면 필드명을 바꿔도 값을 찾을 수 있게된다.
1 2 3 4 5 6 | public class MyClass { [FormerlySerializedAs("m_MyVariable")] [SerializeField] private string m_ABetterName; } |
예를 들어 필드명을 m_MyVariable -> mABetterName 으로 바꾸고자 할 때, 위와 같이 사용한다.
단, [SerializeField]키워드를 사용하고 있는 변수만 해당 키워드가 적용된다.
만약 클래스 자체가 [Serializable]키워드로 만들어져서 사용되는 경우, [FormerlySerializedAs]가 소용없다.
즉, 이 기능을 수동으로 구현할 필요가 있는데 OnBeforeSerialize, OnAfterDeserialize 이벤트가 그것이다.
이 두 이벤트는 각각 직렬화하기 전, 역직렬화가 끝난 후에 호출되는데 이벤트가 호출될 때 임시변수에 불러온 데이터를 넣고 새로운 필드에 재할당 시켜준다.
OnBeforeSerialize, OnAfterDeserialize 이벤트는 ISerializationCallbackReceiver 인터페이스를 상속받아야 사용이 가능하다.
'Language > Unity' 카테고리의 다른 글
[Unity3D] Failure to initialize 에러 (0) | 2018.11.21 |
---|---|
[Unity3D] Desired shader compiler platform 9 is not available in shader blob (0) | 2018.10.10 |
[Unity3D] 유니티 포커스 벗어났을 때 실행되게 하는법 (1) | 2018.10.05 |
[Unity3D] 안드로이드8.0 오레오(Oreo) 퍼미션 권한 에러 (0) | 2018.07.09 |
[Unity3D] 안드로이드 빌드 에러 : CommandInvokationFailure: Unable to list target platforms (0) | 2018.02.04 |