[Unity] 에디터 프로그래밍 – 데이터 저장

에디터 윈도우를 통해 얻은 input 데이터들은 어떻게 저장을 해야할까.  예전에 커스텀 에디터 윈도우를 띄운 후 변수와 컨테이너에 열심히 값을 담았는데, 다시 에디터 윈도우를 열었을 때 모두 사라져 버려 당황한 적이 있다.  에디터 윈도우를 닫고, 새로 열때 마다 다른 인스턴스가 만들어지기 때문에 에디터 윈도우 클래스에서 선언된 변수는 이전 변수를 가리킬 수 없게 되기 때문이다.  커스텀 윈도우를 여러번 열어보고, 간단히 현재 윈도우의 인스턴스 아이디를 찍어보면 알 수 있다.

윈도우의 인스턴스는 위와 같이 각각 다르다.

그래서, 별도의 공간에 저장하고 싶은 데이터를 저장하고, 윈도우가 다시 열릴 때 저장된 데이터를 읽어와야 한다.  이를 위해 텍스트 파일로 저장을 하거나 유니티의 EditorPrefs를 이용하는데, 간단하게 저장을 하려면 EditorPrefs를 이용하면 편리하다.  EditorPrefs는 유니티 에디터상에 설정 정보들을 저장하는 데 사용되는 클래스이다.  키-벨류 형식으로 저장 및 가져오기가 가능하다.   

저장할 곳을 정했다면, 저장 및 로드 시점을 정해야 한다.  저장 및 로드는 OnDisable과 OnEnable 콜백에서 행해지도록 하면 창이 닫히기 전에 저장되고 창이 열릴 때 로드할 수 있는 적절한 지점이 된다.

그럼 string 변수를 선언하고, input한 데이터가 창이 닫고 열때, 제대로 저장이 되고 로드가 되는지 테스트해보자.

public class CEditorWindow : EditorWindow
{
    private string AVariable;
    
    [MenuItem("Tools/My Editor Window")]
    static void Init()
    {
        var curWindow = EditorWindow.GetWindow(typeof(CEditorWindow));
        var cWindow = ((CEditorWindow)curWindow);
        cWindow.Show();
    }
    protected void OnEnable()
    {
        AVariable = EditorPrefs.GetString("MySavedData");
    }
    protected void OnDisable()
    {
        EditorPrefs.SetString("MySavedData", AVariable);
    }

    private void OnGUI()
    {
        GUILayout.Label("AVariable:" + AVariable);
        AVariable = EditorGUILayout.TextField("input", AVariable);
    }
}

AVariable이라는 string 변수에 TextField에서 입력 받은 값을 세팅하고, EditorPrefs를 통해 저장하고 다시 로드해보았다.

11로 입력한 데이터는 다시 에디터 윈도우를 실행하였을 때도 값이 유지되는 것을 확인 할 수 있다.  이제 에디터 윈도우를 열고 닫아도 값이 유지되도록 틀은 갖추었다.  하지만 모든 변수에 대해 키-벨류로 사용하기엔 사용성이 많이 떨어지기 때문에 좀 더 활용성을 높일 필요가 있다. 

JsonUtility.ToJson API를 사용하면 현재 인스턴스의 public field 즉 시리얼라이즈 되는 값들을 가져올 수 있다.  내부적으로는 Unity Serializer를 이용한다고 하니, Serializable 어트리뷰트가 적용된 클래스나 Monobehaviour도 적용 가능하다.  자세한 사항은 메뉴얼을 참고하면 된다. 

그럼 JsonUtility를 이용하여, public으로 선언된 3개의 변수의 값을 저장하고 로드해보자.

public class CEditorWindow : EditorWindow
{
    public string AVariable;
    public string BVariable;
    public string CVariable;

    [MenuItem("Tools/My Editor Window")]
    static void Init()
    {
        var curWindow = EditorWindow.GetWindow(typeof(CEditorWindow));
        var cWindow = ((CEditorWindow)curWindow);
        cWindow.Show();
    }

    protected void OnEnable()
    {
        var data = EditorPrefs.GetString("MySavedData", JsonUtility.ToJson(this, false));
        JsonUtility.FromJsonOverwrite(data, this);
    }
    protected void OnDisable()
    {
        var data = JsonUtility.ToJson(this, false);
        EditorPrefs.SetString("MySavedData", data);
    }

    private void OnGUI()
    {
        GUILayout.Label("AVariable:" + AVariable);
        GUILayout.Label("BVariable:" + BVariable);
        GUILayout.Label("CVariable:" + CVariable);

        AVariable = EditorGUILayout.TextField("input1", AVariable);
        BVariable = EditorGUILayout.TextField("input2", BVariable);
        CVariable = EditorGUILayout.TextField("input3", CVariable);
    }
}

JsonUtility.ToJson에 this, 즉 현재 인스턴스를 전달하면, 시리얼라이즈되는 데이터들을 json 포맷의 string으로 만들어주고, MySavedData라는 키 네임 아래로 저장이 가능하다.  반대로 로드시에는 MySavedData 키를 통해 데이터를 가져오고 JsonUtility.FromJsonOverwrite API를 통해 현재 인스턴스에 저장된 데이터를 세팅할 수 있다.

3개의 변수가 창을 닫고 열어도 유지되는 것을 확인할 수 있다.  이렇게 하면 하나의 키로도 여러개의 데이터를 저장하는 것이 가능하기 때문에 사용성이 높아진다.  Serializable을 이용하여 클래스를 선언하거나, List를 이용하여 데이터를 여러 개 저장하는 것도 가능하기 때문에 다양한 형태로 활용할 수 있다.

기억할 점은, 시리얼라이즈되지 않는 private 변수나 Dictionary 컨테이너는 위와 같이 this를 이용하여 저장할 수 없기 때문에, 데이터가 시리얼라이즈되도록 변환하거나 다른 방식을 취해야 한다. 


Leave a Reply

Your email address will not be published. Required fields are marked *