오늘은 지난시간에 이어 Json.net으로 Serialize와 Deserialize에 대해 알아보자.

Serialize는 흔히 직렬화라 이야기하기도 한다. 네트워크상에서 Class를 전송하는 등의 입출력을 위한 데이터의 배열 형태로 변환하는 과정을 직렬화라 하며,
반대의 경우 데이터의 배열 형태를 Class로 변환화는 과정을 Deserialize(역직렬화)라 한다.

이때의 데이터 형태는 Byte 또는 string 등의 형식으로 변환 될 수 있다.

암튼 각설하여 지난 시간에 이어서 JSON.NET 모듈을 구해서 Import 하던가 기타 등등의 Json Parser를 구하자.
(코딩 노가다 안하려면 Serialize / Deserialize 지원이 잘되는 모듈로 구하자. 안그럼 개고생은 예약해 놓은 거다)

Import가 완료 되었다면 지난시간에 사용한 Entity Class를 다음과 같이 고쳐주자. (본 예제는 JSON.NET을 사용한다. Windows어플에서 가장 많이 쓰이는 모듈이라서...)
using System.Collections.Generic;
using System.Collections;
using Newtonsoft.Json;

public class TestEntityRoot
{
//JsonProperty로 설정하였다.
 [JsonProperty("SubEntity")]
//Static이므로 Get; Set; 선언으로 입출력이 가능하도록 하였다.
 public static List<SubEntity> SubEntity { get; set; }
}

public class SubEntity
{
 public string x { get; set; }
 public string y { get; set; }
 public List<Sub2Entity> sub { get; set; }
}

public class Sub2Entity
{
 public int z;
}


이제 Json Code가 포함된 Scene 0 에 사용할 코드를 작성하자.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Newtonsoft.Json;

public class Scene0Behavior : MonoBehaviour {
 // Use this for initialization
 void Start () {
//JSON 데이터를 정의한다. x는 a, y는 b로 선언했다.
 string _json = @"{
           ""SubEntity"": [
            {
                       ""x"": ""a"",
                       ""y"": ""b"",
                       ""sub"" : [
                       {""z"" : 0 },
                       {""z"" : 1 }
                       ]
                      }
            ]
            }";
 //할당할때는 아무런 클래스를 선언하고 할당한다.
 var _e = JsonConvert.DeserializeObject<TestEntityRoot>(_json);
 
//접근할때는 엔티티 클래스를 직접 호출한다.
 Debug.Log("Scene 0 입력값시작##############");
 foreach (SubEntity _s in TestEntityRoot.SubEntity)
 {
            Debug.Log(_s.x);
 }
 Debug.Log("Scene 0 입력값끝##############");
//저장된 Json 데이터를 출력하고 다음 레벨을 로딩한다.
 Application.LoadLevel(1);
 }

}


Scene1에 사용될 소스를 다음과 같이 입력한다. Scene0과 다른점은 Json데이터 외에 변하는 것은 없다.
(단지 씬이 변경되더라도 데이터가 살아 있고 새로운 데이터를 넣었을때 어떻게 되는지 보여준는데 그 역할이 있다.)

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Newtonsoft.Json;

public class Scene1Behavior : MonoBehaviour {

 // Use this for initialization
 void Start () {

 string _json = @"{
            ""SubEntity"": [
            {
                       ""x"": ""c"",
                       ""y"": ""d"",
                       ""sub"" : [
                       {""z"" : 2 },
                       {""z"" : 3 }
                       ]
                       }
            ]
            }";
 //할당할때는 아무런 클래스를 선언하고 할당한다.
 var _e = JsonConvert.DeserializeObject<TestEntityRoot>(_json);
 //접근할때는 엔티티 클래스를 직접 호출한다.
 Debug.Log("Scene 1 입력값시작##############");
 foreach (SubEntity _s in TestEntityRoot.SubEntity)
 {
 Debug.Log(_s.x);
 }
 Debug.Log("Scene 1 입력값끝##############");

 Application.LoadLevel(2);

 }

}


마지막으로 Scene2에서 사용될 소스는 데이터를 클리어 하고 입력된 값을 출력하는 코드이다.
using UnityEngine;
using System.Collections;

public class Scene2Behavior : MonoBehaviour {

 // Use this for initialization
 void Start () {
//시작과 동시에 데이터를 날려버린다.
 TestEntityRoot.SubEntity.Clear();
 Debug.Log("Scene 2 입력값시작##############");
 foreach (SubEntity _s in TestEntityRoot.SubEntity)
 {
 Debug.Log(_s.x);
 }
 Debug.Log("Scene 2 입력값끝##############");

 }

}


결과는 다음과 같다.
사용자 삽입 이미지
정상적으로 씬이 변경되더라도 데이터가 살아 있고, 새로운 Serialize 된 데이터가 들어오면 추가된다.

2015/08/11 01:47 2015/08/11 01:47
유니티 3D로 개발하다보면, 파편화된 데이터때문에 유지보수나 관리에 어려움을 겪게 되는데,
그러다 보니 Entity Class를 만들고 Dontdestroy 옵션으로 삭제되지 않는 불멸의 데이터 클래스를 생성해서 사용하는 개발자 분들이 많이 보인다.
그런데, 이렇게 하다보면 결국 소스가 꼬여서 오작동 하거나 관리상 문제로 인해 불편함을 가지고 가는 경우도 많은데 그럴 필요 없이 Static 으로 설정하여 Scene을 넘어가더라도 Data가 살아 있는 Class를 만들고 관리하는 것을 목표로 한다.
(물론 Json을 Serialize 하거나 Deserialize 하는건 다음 편에서 보자)

EntityMaster.cs파일을 하나 만들어서 다음과 같이 추가해두자.
(폴더위치는 관계없다.)

using System.Collections.Generic;
using System.Collections;

//테이블과 같은 클래스이다.
public class TestEntity
{
 public static List<SubEntity> _sub = new List<SubEntity>();
}

//1개 Row라고 생각하면 비교가 쉽다.
public class SubEntity
{
 public string _x;
 public string _y;
//Row안에 또 하나의 테이블을 가질 수 있다.
 public List<Sub2Entity> _sub = new List<Sub2Entity>();
}
//SubEntity에 포함된 클래스이다.
public class Sub2Entity
{
 public int _z;
}


이제 Scene0를 만들어서 MainCamera에 다음과 같은 Script를 만들어 Attach하자.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Scene0Behavior : MonoBehaviour {
 // Use this for initialization
 void Start () {
//새로운 Sub2Entity 객체를 만든다.
 List<Sub2Entity> _sub2 = new List<Sub2Entity>();
//Sub2Entity 타입의 객체에 데이터를 넣는다.
_sub2.Add ( new Sub2Entity { _z = 1 });
//테스트엔티티 테이블에 Row를 추가하고 다음 씬을 호출한다.
 TestEntity._sub.Add(new SubEntity { _x = "a", _y = "b", _sub = _sub2 });
 Application.LoadLevel(1);
 }

}


이제 2번째 씬을 만들고 다음과 같이 코딩한후 MainCamera에 Attach하자.
* 위에서 데이터를 추가한 것이라 별도 설명은 생략한다.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Scene1Behavior : MonoBehaviour {

 // Use this for initialization
 void Start () {
 Debug.Log(TestEntity._sub[0]._x);
 List<Sub2Entity> _sub2 = new List<Sub2Entity>();
 _sub2.Add(new Sub2Entity { _z = 2 });
 TestEntity._sub.Add(new SubEntity { _x = "c", _y = "d", _sub = _sub2 });
 Application.LoadLevel(2);
 }
 
}


마지막으로 3번째 씬을 만들어서 다음과 같이 코딩후 Attach하자.
using UnityEngine;
using System.Collections;

public class Scene2Behavior : MonoBehaviour {

 // Use this for initialization
 void Start () {
//시작하면 데이터를 찍는 역할이 전부다.
 Debug.Log(TestEntity._sub[1]._x);
 Debug.Log(TestEntity._sub[1]._sub[0]._z);
 }
 
}


최종 결과는 다음과 같다.

* 씬을 다음과 같이 3개로 구성했고, MainCamera에는 각각의 Script가 Attach되어 있다.
사용자 삽입 이미지
* 최종 결과는 Debug.Log로 다음과 같이 출력된다.
사용자 삽입 이미지
2015/08/07 02:14 2015/08/07 02:14
씬간 데이터를 이동한다던가...
씬내에서 SQL처럼 데이터를 읽거나 쓰고 싶을때가 있을 것이다.

이전에는 playerprefs 에다가 JSON으로 데이터를 넣어두고 씬마다 리셋했는데... 
연구를 하다보니 씬 이동에도 불편하게 Dontdestroy 옵션을 설정하지 않아도 사라지지 않는 Entity Class 구현방법을 알게되어 포스팅하게 되었다.

(how to get data in other scene on the unity3d player with C# entity class)

먼저 다음과 같이 List 객체를 가지고 있는 Entity라는 정적 Class를 만들자.
e는 _e class 타입의 List이다. (hash 테이블보다는 일반 DB에 가까워서 List를 자주쓴다. 또한 JSON / XML 의 Deserializing / serializing에 유리하기도 하다.)

using System.Collections;
using System.Collections.Generic;

public static class Entity
{
     public static List<_e> e = new List<_e>();
}

public class _e
{
     public string _s;
}



이제 "test1Behavior" 테스트코드를 다음과 같이 작성하고 Main Camera에 스크립트를 Attach시켜둔다.
using UnityEngine;
using System.Collections;

public class test1Behavior : MonoBehaviour {

void Start () {
 Entity.e.Add(new _e { _s = "1234" });
 Application.LoadLevel(1);
 }
}


씬이름을 test1로 저장하고 새로운 씬을 연다.

위의 코드와 비슷하게 다음과 같이 작성하고 새로운 씬의 Main Camera에 스크립트를 Attach시켜두자.

using UnityEngine;
using System.Collections;

public class test2Behavior : MonoBehaviour {

 void Start () {
 foreach (_e x in Entity.e)
 {
 Debug.Log(x._s);
 }
 }
}


마지막으로 test2로 씬을 저장한 후에 

이로써 모든 작업이 완료 되었다.
build setting 에서 test1와 test2 씬을 차례대로 넣어두자.

이제 실행해보면 다음과 같이 test1Behavior에서 넣어두었던 데이터 "1234"가 새로운 씬에서 작동한다.

사용자 삽입 이미지
2015/01/15 16:06 2015/01/15 16:06