Unity3D Serialization


이 저작물은 크리에이티브 커먼즈 저작자표시-비영리-변경금지 2.0 대한민국 라이선스에 따라 이용할 수 있습니다.

===============================================================================

유니티 Serialization에 관한 내용입니다. 번역한 것이라서 오역이 있을 수 있습니다.

번역을 하면서 테스트도 진행을 했는데, 블로그에 있는 내용이 애매한 곳이 많아서 조금 수정한 부분도 있습니다.

그 부분 감안해서 읽어주시면 좋을 것 같습니다.

===============================================================================

출처 – 유니티 본사 블로그

http://blogs.unity3d.com/2012/10/25/unity-serialization/

Unity Serialization

유니티에서 에디터 확장 기능을 이용해서 툴을 만들고, 테스트를 해봤을 때 잘 동작하는 듯해서, 알맞게 정렬된 데이터를 입력합니다.

그리고 Play를 눌러 실행을 해서 동작을 확인하고 정지시킵니다.

그런데 갑자기 툴에 입력했던 모든 데이터가 초기화가 됩니다.

문득 이런 질문들이 머리속에 생겨나죠. “이건 너무 하잖아!” “왜 초기화가 된거지?”.

스스로에서 잘못된 부분이 없었는 지, 어떤 부분이 문제를 발생시키는 지 물어보게 됩니다.

왜 이런일이 발생하는 지 이해하려면, Managed Layer 즉 Mono가 유니티에서 어떤 방식으로 동작하는지를 알아야 합니다. 한번 이해를 하면 훨씬 쉬워집니다.

Assembly가 다시 로드되면 어떤일이 발생할까요?

Play모드를 실행 / 멈춤 또는 스크립트 수정을 할때마다 유니티는 모노 어셈블리, 다시말해서 유니티와 관련된 dll을 다시로드 합니다.

이 단계는 3 단계를 거치게 됩니다.

>  먼저 Managed 메모리 영역에 있는 데이터를 다 빼냅니다. 그리고 유니티 내부 C++ 에서 관리될 수 있는 데이터 형태로 생성합니다.

> 유니티의 Managed 메모리 영역에 관련된 정보, 할당된 메모리를 해제하고 어셈블리를 다시 로드 합니다.

> C++ 에 임시로 저장되었던 데이터를 Managed 메모리 영역에 다시 Serialize합니다.

이게 무슨 말이냐하면, 여러분의 데이터 구조 / 정보들이 어셈블리가 다시 로드될 때에도 살아남게 하기 위해서는 유니티의 C++ 영역에서 Serialize가능해야 한다는 의미입니다.

이 과정을 거치면, 여러분의 데이터를 에셋 파일로 저장을하고 나중에 다시 불러올 수 있다는 의미도 됩니다. (약간의 수정이 필요)

유니티의 Serialization을 이용하려면 어떻게 해야하나요?

유니티 Serialization을 이해하는 가장 쉬운 방법 중 하나는 예제를 통해 배우는 방법입니다.

단순한 에디터 윈도우로 예제를 시작해보겠습니다.

이 윈도우는 에셈블리가 다시 로드 되어 정보가 유지되어야하는 클래스의 참조를 가집니다.

using UnityEngine;
using UnityEditor;

public class MyWindow : MonoBehaviour
{
    private SerializeMe m_SerializedThing;
    [MenuItem ("Window/Serialization")] 
    static void Init()
    { 
        GetWindow(typeof(MyWindow)); 
    } 

    void OnEnable()
    {
        hideFlags = HideFlags.HideAndDontSave; 
        if (m_SerializedThing == null) 
        m_SerializedThing = new SerializeMe();
    }

    void OnGUI()
    {
        GUILayout.Label("Serialized Things", EditorStyles.boldLabel);
        m_SerializedThing.OnGUI();
    }
}
using UnityEditor;

public struct NestedStruct
{
    private float m_StructFloat;
    public void OnGUI()
    {
        m_StructFloat = EditorGUILayout.FloatField("Struct Float", m_StructFloat);
    }
}

public class SerializeMe
{
    private string m_Name;
    private int m_Value;

    private NestedStruct m_Struct;

    public SerializeMe()
    {
        m_Struct = new NestedStruct();
        m_Name = "";
    }

    public void OnGUI()
    {
        m_Name = EditorGUILayout.TextField("Name", m_Name);
        m_Value = EditorGUILayout.IntSlider("Value", m_Value, 0, 10);

        m_Struct.OnGUI();
    }
}

 

스크립트를 생성하고, 유니티 에디터 메뉴에서 Window -> Serialization 창을열고 각각의 항목에 값을 수정한 뒤에, Play를 클릭하고 다시 정지를 시키면 여러분이 수정하신 모든 값들이 날라갑니다.

 

어셈블리를 다시 로드하는 과정에서 “m_SerialziedThing”에 대한 참조가 사라지기 때문입니다. Serialize되어야한다고 표시해두지 않았기 때문입니다.

스크립트가 Serialization 되도록 하기위해서 해줘야 할 몇가지 작업이 있습니다.

MyWindow.cs :

> “m_SerializedThing” 필드에 [SerializeField] Attribute를 추가해 줍니다. 이 항목이 붙은   클래스는 유니티에서 어셈블리를 다시 로드하거나 혹은 비슷한 이벤트가 있을 때  Serialization해야한다고 인지합니다.

SerializeMe.cs :

> “SerializeMe” 클래스에 [Serializable] Attribute를 추가해 줍니다. 이렇게 하면 이 클래스는 Serialization 이 가능해집니다.

> “NestedStruct” 구조체에 [Serializable] Attribute를 추가해 줍니다.

> Serialization 되기 원하는 모든 필드에 [SerializeField] Attribute를 추가합니다.

위에서 설명한 항목들을 추가한 뒤에 Serialization 윈도우를 열고 값을 수정합니다.

이제는 어셈블리가 다시 로드가 되어도 여러분이 수정한 값이 유지되는 것을 확인하실 수 있을 겁니다. 그런데 구조체에 속한 필드 값은 그렇지가 않습니다.

여기서 주의해야할 중요한 점이 있는데, 구조체는 Serialization 지원이 완벽하지 않습니다.

“NestedStruct” 를 구조체에서 클래스로 변경을 해서 이 문제를 해결해 보겠습니다.
변경된 내용은 다음과 같습니다.

using UnityEditor;
using UnityEngine'
using System;

[Serializable]
public struct NestedStruct
{
    [SerializeField]
    private float m_StructFloat;
    public void OnGUI()
    {
        m_StructFloat = EditorGUILayout.FloatField("Struct Float", m_StructFloat);
    }
}

[Serializable]
public class SerializeMe
{
    [SerializeField]
    private string m_Name;
    [SerializeField]
    private int m_Value;
    [SerializeField]
    private NestedStruct m_Struct;

    public SerializeMe()
    {
        m_Struct = new NestedStruct();
        m_Name = "";
    }

    public void OnGUI()
    {
        m_Name = EditorGUILayout.TextField("Name", m_Name);
        m_Value = EditorGUILayout.IntSlider("Value", m_Value, 0, 10);

        m_Struct.OnGUI();
    }
}

Serialization 규칙

> 구조체를 피한다.

> Serialization 하려고하는 클래스에 [Serializable] Attribute를 추가한다.

> Public 필드는 Serialization 가능하다. ( [Serializable] 클래스의 필드인 경우 )

> Private 필드는 특정 환경에서 Serialization 가능하다. (에디터)

> Private 필드에 [SerializeField] Attribute를 추가하면 Serialization 가능하다.

[NonSerialized] Attribute를 Serialization 원하지 않는 항목에 사용할 수 있다.

 

Scriptable Objects

지금까지 일반 클래스를 Serialization 하는 방법에 대해 알아보았습니다.

불행히도 유니티에서 상속관계가 없는 평범한 클래스를 Serialization에 사용하는 경우 문제가 있습니다. 예제로 확인해 보겠습니다.


using UnityEditor;
using UnityEngine'
using System;

[Serializable]
public class NestedClass
{
    [SerializeField]</pre>
    private float m_StructFloat;
    public void OnGUI()
    {
        m_StructFloat = EditorGUILayout.FloatField("Float", m_StructFloat);
    }
}

[Serializable]
public class SerializeMe
{
    [SerializeField]
    private NestedClass m_Class1;
    [SerializeField]
    private NestedClass m_Class2;

    public void OnGUI()
    {
        if (m_Class1 == null)
            m_Class1 = new NestedClass();
        if (m_Class2 == null)
            m_Class2 = m_Class1;
        m_Class1.OnGUI();
        m_Class2.OnGUI();
    }
} 

이 예제는 유니티 Serialization 시스템에서 문제를 일으킬 수 상황을 확인하기 위해 억지로 만들어본 예제입니다. 두 개의 NestedClass 타입의 필드가 있는 걸 확인할 수 있습니다.

Serialization 윈도우가 처음 열리면 두 개의 필드 항목이 보입니다. 그리고 이 두 개의 필드, m_Class1과 m_Class2는 같은 참조 값을 가리키기 때문에 한 값을 수정하면 다른 값도 바뀝니다.

이제 Play 버튼을 누르고 다시 멈추게해서 어셈블리를 다시 로드 시킵니다…이렇게 하면 같은 곳을 가리키던 참조값이 풀리게 됩니다. (둘 중 하나를 수정해도 다른 값이 바뀌지 않습니다.)

이 현상이 발생하는 이유는 클래스를 단지 [Serialzable] 로 체크한 경우에 동작하는 방식 때문입니다.

보통의 클래스를 Serialization 하는 경우, 유니티는 해당 클래스의 필드를 확인하고 각각을 Serialization 시키는데, 여러 필드에서 한개의 참조를 공유하는 경우에도 각각을 Serialization 시킵니다.

 

이런 방식으로 동작을 하기 때문에 같은 오브젝트를 여러번 Serialization 시키고, Deserialization을 시키면 해당 오브젝트들이 실제는 같은 오브젝트인지 알지 못합니다.

여러분이 복잡한 시스템을 구성하고 있는 경우라면, 클래스 간의 관계가 복잡해지고 서로 정확한 의사소통을 할 수 없기 때문에, 이런 동작방식이 문제를 야기시킬 수도 있습니다.

이 문제를 해결하기 위해서 설명드릴 부분은 ScriptableObjects입니다.

ScriptableObjects는 유니티에서 제공하는 클래스로, 이를 이용하면 참조를 이용해서, Serialization하기 때문에 정확하게 동작을 합니다.

 

따라서 ScriptableObjects를 이용하면 복잡한 클래스 구조로 사용하더라도, 여러분이 예상하시는 결과를 얻을 수 있습니다.

유니티 내부적으로는 ScriptableObjects와 MonoBehaviours는 동일 하지만, 다른점은 ScriptableObjects는 GameObject에 부착하지 않고 사용할 수 있습니다.

또한 Serialization을 구성하는 데 매우 좋습니다.
이제 위에서 발생한 문제를 제대로 고친 예제를 확인해 보겠습니다.


using System;
using UnityEditor;
using UnityEngine;

[Serializable]
public class NestedClass : ScriptableObject
{
    [SerializeField]
    private float m_StructFloat;

    public void OnEnable()
    {
        hideFlags = HideFlags.HideAndDontSave;
    }

    public void OnGUI()
    {
        m_StructField = EditorGUILayout.FloatField("Float", m_StructField);
    }
}

[Serializable]
public class SerializeMe
{
    [SerializeField]
    private NestedClass m_Class1;

    [SerializeField]
    private NestedClass m_Class2;

    public SerializeMe()
    {
        m_Class1 = ScriptableObject.CreateInstance<NestedClass>();
        m_Class2 = m_Class1;
    }

    public void OnGUI()
    {
        m_Class1.OnGUI();
        m_Class2.OnGUI();
    }
}

세가지 수정사항이 있습니다.

> NestedClass를 ScriptableObject를 상속받도록 수정

> 생성자를 호출하는 대신, CreateInstance<>를 이용해서 인스턴스를 생성

> hide flag를 설정했습니다. – 이 부분은 나중에 설명을 드리겠습니다.

이렇게 수정을 거치면, NestedClass는 한번만 Serialize되고 이 클래스를 가리키는 참조값들은 모두 같은 값을 갖게 됩니다.

 

ScriptableObject 초기화

이제 외부에서 참조를 해야하는 복잡한 데이터 구조에서 Serialization을 구현하는 경우에 ScriptableObject를 사용하면 좋다는 점을 알게 되었습니다.

 

그럼 이제 스크립트단에서 ScriptableObject를 어떻게 사용해야 하는지에 대해 알아볼 차례 입니다.

첫째로 확인해야할 사항은 유니티 시스템에서의 초기화 방법입니다.

  1. ScriptableObject에서 생성자 호출
  2. 유니티 내부 C++ 부분에서 데이터가 해당 오브젝트로 Serialization 진행

(데이터가 있는 경우)

  1. ScriptableObject에서 OnEnable() 호출

이 과정을 진행하기전에 주의해야할 사항들이 있습니다.

> Serialzation 시스템에 의해 값이 덮어써질 위험이 있기 때문에, 생성자에서 초기화 작업을 하는 것은 좋지 않습니다.

> Serialization은 생성이 완료된 이후에 진행이 되기 때문에, Serialization이 완료된 이후에 세부설정을 하는 것이 좋습니다.

> OnEnable() 함수에서 초기화 작업을 하는 것이 가장 좋은 방법입니다.

‘SerializeMe’ 클래스를 조금 수정해보겠습니다.

아래 예제코드는 ScriptableObject 초기화 작업의 좋은 예를 보여줍니다.


using System;
using UnityEditor;
using UnityEngine;

// also updated the Window to call CreateInstance instead of the constructor.
[Serializable]
public class SerializeMe :ScriptableObject
{
    [SerializeField]
    private NestedClass m_Class1;

    [SerializeField]
    private NestedClass m_Class2;

    public void OnEnable()
    {
        hideFlags = HideFlags.HideAndDontSave;
        if (m_Class1 == null)
        {
            m_Class1 = CreateInstance<NestedClass>();
            m_Class2 = m_Class1;
        }
    }

    public void OnGUI()
    {
        m_Class1.OnGUI();
        m_Class2.OnGUI();
    }
}

전체적으로 거의 수정된 부분이 없는 것처럼 보이지만, ScriptableObject를 상속받도록 수정이 되었고, 생성자에서 초기화를 하지않고, OnEnable()함수에서 초기화를 진행합니다.

감지하기 힘든 부분이지만, 집고 넘어가야할 중요한 부분은 OnEnable()함수는 Serialization이 진행된 후에 호출이 된다는 점 입니다

바로 이런 특징 때문에 [SerializeField]가 Null인지 아닌지 확인할 수 있습니다.

Null인 경우 초기화가 처음 진행된다는 점을 나타내기 때문에, 인스턴스를 생성해줘야 합니다.

Null이 아닌 경우에는 메모리로 부터 로드되었다는 것을 나타내기 때문에 생성할 필요가 없습니다.

또한 OnEnable() 함수내에서 다른 초기화관련 함수를 호출할 수도 있고, 기존의 생성자에서 초기화를 하듯이 다른 Private / non serialized 필드에 관한 초기화 작업도 진행할 수 있습니다.

 

HideFlags

ScriptableObject를 사용하는 예제코드를 보면 ‘higeFlags’를 HideFlags.HideAndDontSave로 설정한 것을 확인할 수 있습니다.

씬 내의 루트(root)를 갖지 않는 특정 데이터 구조를 저장할 때 요구되는 설정항목 입니다.

이제 유니티에서 씬 로딩이 어떻게 이루어지는 지 확인해보겠습니다.

내부적으로 씬이 로드되면, 유니티는 Resources.UnloadUnUsedAssets함수를 호출합니다.

에셋을 참조하고 있는 부분이 없다면, 가비지 컬렉터가 확인합니다.

GC는 씬을 루트(root)로 사용해서 그 계층구조를 검사해서 가비지 컬렉션 대상을 찾습니다.

HideAndDontSave flag로 설정 하게되면, 유니티가 해당 오브젝트를 루트(root) 오브젝트로 다루게 됩니다.

그렇기 때문에 어셈블리가 다시 로드되어도 해당 오브젝트가 사라지지 않게 됩니다.

해당 오브젝트는 Destroy()함수를 호출해서 제거할 수도 있습니다.

 

ScriptableObject 사용 규칙

> ScriptableObejct는 한번만 Serialize 되고, 이 값을 참조할 수 있습니다.

> OnEnable() 함수를 이용해서 ScriptableObejct를 초기화 합니다.

> ScriptableObejct의 생성자는 사용을 자제하고, 대신 CreateInstance함수를 사용합니다.

> 다중 구조체 (Nested Structure)의 경우, 한번만 참조가 가능하기 때문에, ScriptableObject 사용을 자제하는 것이 좋습니다. 오버헤드가 발생할 수 있습니다.

> 여러분의 ScriptableObject가 루트(root)로 설정되어 있지 않은경우, hideFlags를 HideAndDontSave로 설정.

 

상속관계 없는 클래스 배열 Serialization

예제를 보면서 설명해 드리겠습니다.


using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

[Serializable]
public class BaseClass
{
    [SerializeField]
    private int m_IntField;

    public void OnGUI()
    {
        m_IntField = EditorGUILayout.IntSlide("IntField", m_IntField, 0, 10);
    }
}

[Serializable]
public class SerializeMe :ScriptableObject
{
    [SerializeField]
    private List<BaseClass> m_Instances;
    public void OnEnable()
    {
        hideFlags = HideFlags.HideAndDontSave;
        if (m_Instances == null)
            m_Instances = new List<BaseClass>();
    }

    public void OnGUI()
    {
        foreach (var instance in m_Instances)
            instance.OnGUI();
        if (GUILayout.Button("Add Simple"))
        {
            m_Instances.Add(new BaseClass());
        }
    }
}

예제의 SerializeMe 클래스는 BaseClass의 리스트를 가지고 있고, ‘Add Simple’버튼을 누르면 인스턴스를 생성해서 리스트에 추가합니다.

SerializeMe 클래스는 serialization이 제대로 동작되도록 수정되었기 때문에, 리스트를 추가하는 부분도 동작합니다.

유니티는 리스트가 Serialization되도록 표시되어 있는 것을 확인하고 리스트 내의 각 항목에 대해서 Serialization을 진행합니다.

 

일반적인 배열 Serialization (상속 관계 있는 클래스 배열)

예제코드를 조금 수정해서, 리스트가 상속관계가 있는 클래스의 부모 클래스와 자식 클래스를 갖도록 했습니다.


using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

[Serializable]
public class BaseClass
{
    [SerializeField]
    private int m_IntField;

    public virtual void OnGUI()
    {
        m_IntField = EditorGUILayout.IntSlide("IntField", m_IntField, 0, 10);
    }
}

[Serializable]
public class ChildClass : BaseClass
{
    [SerializeField]
    private float m_FloatField;

    public override void OnGUI()
    {
        base.OnGUI();
        m_FloatField = EditorGUILayout.Slider("FloatField", m_FloatField, 0f, 10f);
    }
}

[Serializable]
public class SerializeMe :ScriptableObject
{
    [SerializeField]
    private List<BaseClass> m_Instances;
    public void OnEnable()
    {
        if (m_Instances == null)
            m_Instances = new List<BaseClass>();

        hideFlags = HideFlags.HideAndDontSave;
    }

    public void OnGUI()
    {
        foreach (var instance in m_Instances)
            instance.OnGUI();

        if (GUILayout.Button("Add Base"))
            m_Instances.Add(new BaseClass());
        if (GUILayout.Button("Add Child"))
            m_Instances.Add(new ChildClass());
    }
}

예제가 확장되어 ChildClass가 추가되었습니다. 하지만 BaseClass를 사용해서 Serialization 하도록 구현해 놓은 상태입니다.

한개 이상의 ChildClass 인스턴스를 생성해도 BaseClass가 제대로 동작하는 것 같아 보입니다. 하지만 어셈블리를 다시 로드하게되면 문제가 발생합니다.

어셈블리를 다시 로드하게 되면 모든 인스턴스들이 BaseClass 인스턴스가 되어 ChildClass의 정보를 잃게 됩니다.

해당 인스턴스들은 Serialization 시스템에 의해서 공유가 됩니다.
Serialization 시스템의 이러한 제한사항을 극복하기 위해서 한번 더 ScriptableObject를 사용합니다. 그리고 각각의 클래스를 다른 스크립트로 분리했습니다.


using System;
using UnityEngine;
using UnityEditor;

[Serializable]
public class MyBaseClass : ScriptableObject
{
    [SerializeField]
    private int m_IntField;

    public void OnEnable()
    {
        hideFlags = HideFlags.HideAndDontSave;
    }

    public virtual void OnGUI()
    {
        m_IntField = EditorGUILayout.IntSlide("IntField", m_IntField, 0, 10);
    }
}

[Serializable]
public class ChildClass : MyBaseClass
{
    [SerializeField]
    private float m_FloatField;

    public override void OnGUI()
    {
        base.OnGUI();
        m_FloatField = EditorGUILayout.Slider("FloatField", m_FloatField, 0f, 10f);
    }
}
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

[Serializable]
public class SerializeMe :ScriptableObject
{
    [SerializeField]
    private List<MyBaseClass> m_Instances;
    public void OnEnable()
    {
        if (m_Instances == null)
            m_Instances = new List<MyBaseClass>();

        hideFlags = HideFlags.HideAndDontSave;
    }

    public void OnGUI()
    {
        foreach (var instance in m_Instances)
        {
            instance.OnGUI();
            EditorGUILayout.Space();
        }
        if (GUILayout.Button("Add Base"))
            m_Instances.Add(CreateInstance<MyBaseClass>());
        if (GUILayout.Button("Add Child"))
            m_Instances.Add(CreateInstance<ChildClass>());
    }
}

실행을 한 후에, 값을 수정하고 어셈블리를 다시 로드를 해도 상속관계가 있음에도 값이 잘 보존되어 있는 것을 확인할 수 있습니다.

ScriptableObject가 아닌 [Serializable] 클래스는 그 위치에 Serialization이 진행 되지만, ScriptableObject는 외부에 Serialization이 진행되고, 참조 값이 리스트에 전달됩니다.

 

추상 클래스 Serialization

이제 상속관계가 있는 리스트의 Serialization이 가능하다는 점도 확인을 했으니, 이제 추상클래스에 대해 알아보겠습니다.


using System;
using UnityEngine;
using UnityEditor;

[Serializable]
public abstract class MyBaseClass : ScriptableObject
{
    [SerializeField]
    private int m_IntField;

    public void OnEnable()
    {
        hideFlags = HideFlags.HideAndDontSave;
    }

    public abstract void OnGUI();
}

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

[Serializable]
public class ChildClass : MyBaseClass
{
    [SerializeField]
    private float m_FloatField;

    public override void OnGUI()
    {
        m_IntField = EditorGUILayout.IntSlider("IntField", m_IntField, 0, 10);
        m_FloatField = EditorGUILayout.Slider("FloatField", m_FloatField, 0f, 10f);
    }
}

[Serializable]
public class SerializeMe :ScriptableObject
{
    [SerializeField]
    private List<ChildClass> m_Instances;
    public void OnEnable()
    {
        if (m_Instances == null)
            m_Instances = new List<ChildClass>();

        hideFlags = HideFlags.HideAndDontSave;
    }

    public void OnGUI()
    {
        foreach (var instance in m_Instances)
            instance.OnGUI();

        if (GUILayout.Button("Add Child"))
            m_Instances.Add(CreateInstance<ChildClass>());
    }
}

이 예제코드는 이전 예제와 비슷하게 동작을 합니다. 하지만 위험합니다.

그 이유를 설명해 드리겠습니다.

CreateInstance<>() 함수는 ScriptableObject를 상속받는 타입의 인자를 요구하고, ‘MyBaseClass’는 실제로 ScriptableObject를 상속받고 있습니다. 다시말해 추상 클래스인 MyBaseClass의 인스턴스를 m_Instance 리스트에 추가하는 것이 가능합니다.

이 과정을 거치고, 추상 메소드에 접근을 하려는 순간에 함수 구현부가없기 때문에 문제가 발생합니다. 이 예제의 경우 OnGUI메소드가 구현이 되어 있지 않습니다.

ScriptableObject를 상속받아서 구현을 하는 경우에, 추상클래스의 Serialization이 가능은 합니다. 하지만 권장하는 방법은 아닙니다.

개인적으로는 가상 메소드가 없는 기본 클래스를 구현해서 사용하는 걸 권장합니다.

이렇게 사용하면 문제가 발생할 일이 없기때문입니다.

 

ScriptableObject는 언제 씬 또는 프리펩 파일에 저장이 되나요?

게임 오브젝트와 해당 게임오브젝트가 가지고 있는 컴포넌트들은 기본적으로 씬에 저장이됩니다.

스크립트로부터 생성된 에셋 타입 (재질 / 메쉬 / 애니메이션 클립 / SerializedObject)들은 게임 오브젝트 또는 컴포넌트가 해당 에셋 타입을 참조하는 경우, 씬에 저장됩니다.

또한 에셋 타입은 AssetDataBase.CreateAsset을 이용해서 Asset으로 등록할 수 있습니다.

이 경우, 씬에 저장되지는 않고 단순히 참조만 하게됩니다. 어떤 에셋 타입 또는 게임오브젝트 타입이 HideAndDontSave로 체크되어있는 경우 또한 씬에 저장되지 않습니다.

 

내용 끝까지 읽어주셔서 감사합니다.
배너 클릭은 저에게 많은 힘이 됩니다.
감사합니다 🙂

RonnieJ

프리랜서 IT강사로 활동하고 있습니다. 게임 개발, C++/C#, 1인 기업에 관심이 많습니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

Please turn AdBlock off

Notice for AdBlock users

Please turn AdBlock off