【问题标题】:Unity ScriptableObject Polymorphic Arrays SerializationUnity ScriptableObject 多态数组序列化
【发布时间】:2022-02-05 02:16:50
【问题描述】:

主要问题是,我正在尝试在 ScriptableObject 中使用多态列表,但在重新启动 Unity 时其内容会丢失;代码如下。


所以我有一个基类:

[Serializable]
public class BaseAction : ScriptableObject
{
    public virtual void OnGUI()
    {
    }

    public virtual void ExecuteAction()
    {
    }
}

还有一个带有简单字符串属性的继承类:

[Serializable]
public class DebugLogAction : BaseAction
{
    [SerializeField]
    private string debugText = default;

    public override void OnGUI()
    {
        debugText = EditorGUILayout.TextField(debugText);
    }

    public override void ExecuteAction()
    {
        Debug.Log(debugText);
    }
}

它们都在一个包含 BaseAction 列表的 ScriptableObject 中重新组合:

[Serializable]
[CreateAssetMenu(fileName = "NewActionContainer", menuName = "ScriptableObjects/Action Container")]
public class ActionContainer : ScriptableObject
{
    [SerializeField]
    private List<BaseAction> actions = default;

    public void OnEnable()
    {
        if (actions == null)
        {
            actions = new List<BaseAction>();
        }
    }

    public void OnInspectorGUI()
    {
        foreach (var action in actions)
        {
            action.OnGUI();
        }

        if (GUILayout.Button("Add Debug Log Action"))
        {
            actions.Add(CreateInstance<DebugLogAction>());
        }
    }
}

我已经为 ActionContainer ScriptableObject 编写了一个自定义编辑器,该编辑器工作正常,显示用于添加多个可编辑输入字段的操作的按钮:

[CustomEditor(typeof(ActionContainer)), CanEditMultipleObjects]
public class CustomActionContainerInspector : Editor
{
    private ActionContainer actionContainer;

    public void OnEnable()
    {
        actionContainer = (ActionContainer)target;
    }

    public override void OnInspectorGUI()
    {
        actionContainer.OnInspectorGUI();
    }
}

但是,在 Unity 重新启动时,ScriptableObject 的内容消失了(程序集重建很好,播放/停止游戏也会保留数据)。

我尝试向 BaseAction 添加一个字段来检查值是否刚刚从继承类中剪掉,但是没有; ActionContainer 中的整个操作列表都清空了。

我认为序列化或反序列化列表本身的元素存在问题,因为它是多态的,但我不知道如何调试或解决问题;互联网搜索也没有让我找到解决方案。

【问题讨论】:

    标签: c# unity3d serialization polymorphism


    【解决方案1】:

    首先,一般来说,您不应该调用您的方法OnGUI,因为这也是MonoBehaviour 中的built-in 消息,可能会导致混淆。

    我认为你的主要缺陷是 dirty 状态。

    如果您在更改对象后没有将其标记为脏,那么您的更改将无法正确保存。

    我认为快速解决此问题的方法是使用EditorUtility.SetDirty

    public override void OnGUI()
    {
        EditorGUI.BeginChangeCheck();
        debugText = EditorGUILayout.TextField(debugText);
        if(EditorGUI.EndChangeCheck())
        {
            EditorUtility.SetDirty(this);
        }
    }
    

    或者如果您想正确支持撤消/重做,那么请使用Undo.RecordObject

    public override void OnGUI()
    {
        EditorGUI.BeginChangeCheck();
        var newDebugText = EditorGUILayout.TextField(debugText);
        if(EditorGUI.EndChangeCheck())
        {
            Undo.RecordObject(this, "changed debug text");
            debugText = newDebugText;
        }
    }
    

    另见


    对于

    if (GUILayout.Button("Add Debug Log Action"))
        {
            //either
            Undo.RecordObject(this, "add element");
            actions.Add(CreateInstance<DebugLogAction>());
            //or 
            //EditorUtilty.SetDirty(this);
        }
    

    【讨论】:

    • 感谢您的回答! OnGUI 评论是公平的,因为我最初尝试使用 MonoBehavior 并且将被更改,所以这是一个剩余的评论。我尝试了 SetDirty() 修复,唯一可以帮助进行更改检查的地方是 ActionContainer 的“OnInspectorGUI()”;该列表现在可以保存自己,但是在重新启动 Unity 时,它会反序列化为一个充满 Null 值的列表(似乎列表中的项目本身无法正确反序列化);我尝试在列表本身上使用 [SerializeReference] 属性,但不幸的是它没有帮助。
    • @AlexCosarca 您是否也尝试过使用[SerializeReference]?见this similar question
    • 我已经尝试过对象列表上的属性,但不幸的是它没有任何区别。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-03
    • 2018-12-18
    相关资源
    最近更新 更多