[英]ScriptableObject generic list makes all derived instances to base type when serializing in unity
I'm trying to make a stat system library to use in as many games as possible in the future, but unity's serialization got in the way. 我正在尝试制作一个统计信息系统库,以便将来在尽可能多的游戏中使用,但是unity的序列化妨碍了这一工作。
Knowing how awful is the serialization in unity and that derived instances get transformed to base type during serialization, I decided to make the base class derive from ScriptableObject, but it does not work. 知道序列化的统一性是多么糟糕,并且派生的实例在序列化过程中被转换为基类型,因此我决定使基类派生自ScriptableObject,但它不起作用。 My code was becoming a bit messy at this point so I decided to make a much simpler test version from scratch, by the book, as explained here: 此时,我的代码变得有些混乱,因此我决定从书开始重新编写一个简单得多的测试版本,如下所述:
https://forum.unity.com/threads/serialization-best-practices-megapost.155352/ https://forum.unity.com/threads/serialization-best-practices-megapost.155352/
But that didn't work either. 但这也不起作用。
The base class: 基类:
[System.Serializable]
public class BaseClass : ScriptableObject
{
[SerializeField]
private string m_Name;
[SerializeField]
public string Name { get => m_Name; set => m_Name = value; }
public static BaseClass NewInstance()
{
BaseClass b = CreateInstance<BaseClass>();
b.Name = string.Empty;
return b;
}
public static BaseClass NewInstance(string name)
{
BaseClass b = CreateInstance<BaseClass>();
b.Name = name;
return b;
}
}
The derived class: 派生类:
[System.Serializable]
public class DerivedClass : BaseClass
{
[SerializeField]
private string m_Value;
[SerializeField]
public string Value { get => m_Value; set => m_Value = value; }
public new static DerivedClass NewInstance()
{
DerivedClass d = CreateInstance<DerivedClass>();
d.Name = string.Empty;
d.Value = string.Empty;
return d;
}
public static DerivedClass NewInstance(string name, string value)
{
DerivedClass d = CreateInstance<DerivedClass>();
d.Name = name;
d.Value = value;
return d;
}
}
And finally the collection class: 最后是集合类:
[System.Serializable] [CreateAssetMenu(menuName = "CollectionA")]
public class CollectionA : ScriptableObject
{
[SerializeField]
private List<BaseClass> m_TestList;
[SerializeField]
public List<BaseClass> TestList { get => m_TestList; set => m_TestList = value; }
public static CollectionA NewInstance()
{
CollectionA c = CreateInstance<CollectionA>();
c.TestList = new List<BaseClass>();
return c;
}
public List<T> GetAllWithType<T>()
{
try { return TestList.OfType<T>().ToList<T>(); }
catch { return new List<T>(); }
}
}
I am creating instances and checking the list like this: 我正在创建实例并检查列表,如下所示:
private void Update()
{
if (Input.GetKeyDown("k"))
{
print("Derived stats:");
List<DerivedClass> derived = collection.GetAllWithType<DerivedClass>();
foreach (DerivedClass t in derived)
{ print(t.Name + " | " + t.Value); }
}
if (Input.GetKeyDown("p"))
{
DerivedClass d = DerivedClass.NewInstance("Hey", "Hello");
collection.TestList.Add(d);
}
}
The custom editor I am using: 我正在使用的自定义编辑器:
[CustomEditor(typeof(CollectionA))]
public class CollectionAEditor : Editor
{
private CollectionA collection;
private List<DerivedClass> derived;
struct derivedValues
{
public string name, value;
}
derivedValues addDerived = new derivedValues();
public override void OnInspectorGUI()
{
if (target is CollectionA)
collection = (CollectionA)target;
if (collection != null)
{
DrawInspector();
}
}
private void DrawInspector()
{
derived = collection.GetAllWithType<DerivedClass>();
// title
EditorGUILayout.Space();
GUILayout.Label("CLASS LIST", EditorStyles.largeLabel);
// title
EditorGUILayout.Space();
GUILayout.Label("Derived classes:", EditorStyles.boldLabel);
// layout labels
GUILayout.BeginHorizontal();
GUILayout.Label("Name", GUILayout.MinWidth(35));
GUILayout.Label("Value", GUILayout.MinWidth(35));
GUILayout.EndHorizontal();
// derived classes list
if (derived.Count <= 0)
GUILayout.Label("Class list empty", EditorStyles.centeredGreyMiniLabel);
else
{
foreach (DerivedClass x in derived)
{
GUILayout.BeginHorizontal();
x.Name = GUILayout.TextField(x.Name, GUILayout.MinWidth(35));
x.Value = GUILayout.TextField(x.Value, GUILayout.MinWidth(35));
GUILayout.EndHorizontal();
}
}
// add derived stat
EditorGUILayout.Space();
GUILayout.BeginHorizontal();
GUILayout.Label("Values:", GUILayout.Width(50f));
addDerived.name = GUILayout.TextField(addDerived.name, GUILayout.MinWidth(35));
addDerived.value = GUILayout.TextField(addDerived.value, GUILayout.MinWidth(35));
GUILayout.EndHorizontal();
if (GUILayout.Button("Add derived class"))
{
collection.TestList.Add(
DerivedClass.NewInstance(addDerived.name, addDerived.value));
addDerived = new derivedValues();
}
// default stats title
EditorGUILayout.Space();
GUILayout.BeginHorizontal();
GUILayout.Label("All classes as default:", EditorStyles.boldLabel);
if (collection.TestList == null) Debug.Log("NULL");
GUILayout.Label(collection.TestList.Count.ToString());
GUILayout.EndHorizontal();
EditorGUILayout.Space();
if (GUILayout.Button("Delete all instances"))
collection.TestList.Clear();
GUILayout.BeginHorizontal();
GUILayout.Label("Name", GUILayout.MinWidth(35));
GUILayout.EndHorizontal();
// all classes list
if (collection.TestList.Count <= 0)
GUILayout.Label("Class list empty", EditorStyles.centeredGreyMiniLabel);
else
{
foreach (BaseClass x in collection.TestList)
{
GUILayout.BeginHorizontal();
x.name = GUILayout.TextField(x.name, GUILayout.MinWidth(35));
GUILayout.EndHorizontal();
}
}
}
}
If I press Play and add a new derived instance to the list, it is recognized properly until I press Play again, when the derived instance gets moved to base type. 如果我按Play并将一个新的派生实例添加到列表中,它将被正确识别,直到当派生实例被移到基本类型时再次按Play时。
I tried a lot of things, but none seem to work so thank you for trying to help me. 我尝试了很多事情,但似乎没有任何效果,因此感谢您尝试帮助我。
OK I figured it out. 好,我知道了。 I was not creating assets out of the 2 classes. 我不是在2类中创建资产。 ScriptableObjects as assets can't reference non asset / prefab instances without loosing references apparently, so the data of my list of BaseClass
got lost. 作为资产的ScriptableObjects不能在没有明显丢失参考的情况下引用非资产/预制实例,因此我的BaseClass
列表数据丢失了。
Conclusion: it's not enough to make a ScriptableObject wrapper for ScriptableObject fields that you make an asset out of, but you need to make assets out of the fields too so the reference doesn't get lost. 结论:仅对要使用资产的ScriptableObject字段创建ScriptableObject包装器是不够的,但是您也需要在这些字段之外使用资产,以使引用不会丢失。
@derHugo thank you for your time. @derHugo谢谢您的时间。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.