2015年4月13日 星期一

編輯器開發筆記_正確用List<父類別>, 儲存子類別資料

需求描述:


假設我有以下資料父類別
using UnityEngine;
using UnityEditor;
using System.Collections;
public abstract class DataSuperClass
{
[SerializeField]
public string superStr = "super default str";
public abstract void Draw();
}

以及子類別
using UnityEngine;
using UnityEditor;
using System.Collections;
[System.Serializable]
public class DataSubClass : DataSuperClass
{
[SerializeField]
public string subStr = "sub default string";
public override void Draw ()
{
subStr = EditorGUILayout.TextField("subStr", subStr);
}
}
view raw DataSubClass.cs hosted with ❤ by GitHub

我在測試類別上宣告了一個父類別型態的List
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class TestClass : MonoBehaviour
{
[SerializeField]
public List<DataSuperClass> list = new List<DataSuperClass>();
}
view raw TestClass.cs hosted with ❤ by GitHub

同時我實做了TestClass的Inspector內容
using UnityEngine;
using UnityEditor;
using System.Collections;
[CustomEditor(typeof(TestClass))]
public class TestClassEditor : Editor
{
public override void OnInspectorGUI ()
{
TestClass tc = (TestClass)target;
for(var i = 0; i < tc.list.Count; i++)
{
tc.list[i].Draw();
}
if(GUILayout.Button("Add"))
{
tc.list.Add(new DataSubClass());
}
}
}

此時我將TestClass掛載到任意GameObject上
使用我自訂的Add鈕去新增數個DataSubClass到TestClass的List上
修改了裡頭的字串後, 將Unity存檔重開

重開後會赫然發現, DataSubClass的資料全不見了
這不是我所期盼的, 資料不應該遺失

該如何解決?

因此我想了很多關鍵字, 想辦法扒文
終於讓我找到修正這個錯誤的關鍵了

原文的解釋是這樣的
Unity determine the instance type via the variable type. If you store a subclass in a base class variable it gets serialized as base class.

However there is a way to use inheritance with serialization: ScriptableObject.

Your base class has to be derived from ScriptableObject. In this case Unity will remember the true type when you store a sub / child class in a base-class variable.

簡單翻譯就是:
Unity只認變數型別, 所以你如果把子類別實體存到父類別變數上去
那Unity在進行序列化儲存的時候, 就只會存父類別的內容

然而, Unity提供了一個很便利的解: ScriptableObject

只要你的父類別繼承了ScriptableObject
那麼他就能正確進行辨識了

下面附上修改後的父類別程式碼
using UnityEngine;
using UnityEditor;
using System.Collections;
public abstract class DataSuperClass : ScriptableObject
{
[SerializeField]
public string superStr = "super default str";
public abstract void Draw();
}


補充:
本來研究這個是為了要在StateMachineBehaviour搞花樣的
沒想到實際搬到StateMachineBehaviour後才發現
這個方法只能用在MonoBehaviour體系底下的

所以如果你想把這個方法用到StateMachineBehaviour
請及早收手吧, 歪路我走過了, 此路不通

沒有留言:

張貼留言