You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义编辑器中复杂对象字段的预制体修改无法保存

Unity自定义编辑器字典数据无法保存的解决方案

问题根源:
Unity的原生序列化系统不支持Dictionary类型,即便给字典字段加上[SerializeField]也无法完成正确的序列化/反序列化,导致预制体重新加载时字典值丢失。另外List<KeyValuePair<string, float>>也无法正常工作,因为KeyValuePair是值类型,修改其Value时Unity检测不到列表内部的变化,序列化系统不会记录这些修改。

下面是具体的解决步骤:

1. 替换Dictionary为可序列化的自定义键值对列表

创建一个可序列化的自定义键值对类,让Unity能够正确序列化列表数据:

[System.Serializable]
public class SerializableFloatKeyValue
{
    public string key;
    public float value;

    public SerializableFloatKeyValue(string key, float value)
    {
        this.key = key;
        this.value = value;
    }
}

修改MovementVariableModifierData类,用自定义列表替代Dictionary:

[System.Serializable]
public class MovementVariableModifierData
{
    [ItemType]
    public string modifierItemID = "";
    public int modifierItemIdx;

    // 替换Dictionary为可序列化的自定义列表
    public List<SerializableFloatKeyValue> multipliers = new List<SerializableFloatKeyValue>();
    public List<SerializableFloatKeyValue> addends = new List<SerializableFloatKeyValue>();

    // 运行时快速转成Dictionary的工具方法
    public Dictionary<string, float> GetMultipliersDict()
    {
        var dict = new Dictionary<string, float>();
        foreach (var kv in multipliers)
        {
            dict[kv.key] = kv.value;
        }
        return dict;
    }

    // 编辑器中快速查找/添加键值对的方法
    public SerializableFloatKeyValue GetOrAddMultiplier(string key)
    {
        var kv = multipliers.Find(x => x.key == key);
        if (kv == null)
        {
            kv = new SerializableFloatKeyValue(key, 1.0f);
            multipliers.Add(kv);
        }
        return kv;
    }
}

2. 使用SerializedProperty处理编辑器修改(推荐)

Unity编辑器推荐使用SerializedProperty来操作对象字段,它会自动处理序列化脏标记,确保修改被正确保存:

// Custom Editor Code:

// 获取序列化对象的Modifiers属性
SerializedProperty modifiersProp = serializedObject.FindProperty("Modifiers");

for (int i = 0; i < modifiersProp.arraySize; i++)
{
    SerializedProperty modifierProp = modifiersProp.GetArrayElementAtIndex(i);
    
    // 处理普通字段(modifierItemIdx和modifierItemID)
    SerializedProperty itemIdxProp = modifierProp.FindPropertyRelative("modifierItemIdx");
    SerializedProperty itemIDProp = modifierProp.FindPropertyRelative("modifierItemID");

    int newIdx = EditorGUILayout.Popup(new GUIContent("Item ID:"), itemIdxProp.intValue, itemTypes);
    if (newIdx != itemIdxProp.intValue)
    {
        itemIdxProp.intValue = newIdx;
        itemIDProp.stringValue = itemTypes[newIdx];
    }

    // 处理multipliers列表中的Key1字段
    SerializedProperty multipliersProp = modifierProp.FindPropertyRelative("multipliers");
    int key1Index = -1;

    // 查找已存在的Key1条目
    for (int j = 0; j < multipliersProp.arraySize; j++)
    {
        SerializedProperty kvProp = multipliersProp.GetArrayElementAtIndex(j);
        if (kvProp.FindPropertyRelative("key").stringValue == "Key1")
        {
            key1Index = j;
            break;
        }
    }

    // 如果不存在则新增条目
    if (key1Index == -1)
    {
        multipliersProp.InsertArrayElementAtIndex(multipliersProp.arraySize);
        SerializedProperty newKvProp = multipliersProp.GetArrayElementAtIndex(multipliersProp.arraySize - 1);
        newKvProp.FindPropertyRelative("key").stringValue = "Key1";
        newKvProp.FindPropertyRelative("value").floatValue = 1.0f;
        key1Index = multipliersProp.arraySize - 1;
    }

    // 修改Key1的值
    SerializedProperty key1ValueProp = multipliersProp.GetArrayElementAtIndex(key1Index).FindPropertyRelative("value");
    key1ValueProp.floatValue = EditorGUILayout.FloatField(new GUIContent("Key 1:"), key1ValueProp.floatValue);

    // 其他字段同理处理...
}

// 应用所有属性修改
serializedObject.ApplyModifiedProperties();

3. 若坚持直接修改字段,需手动标记脏状态

如果不想用SerializedProperty,直接修改字段后必须手动告诉Unity数据已更改,否则不会保存:

// Custom Editor Code:

// 读取旧值并复制到新列表
var tmpModifiers = new List<MovementVariableModifierData>(Modifiers);

for (int i = 0; i < tmpModifiers.Count; i++)
{
    // 修改普通字段(正常工作部分保留)
    tmpModifiers[i].modifierItemIdx = EditorGUILayout.Popup(new GUIContent("Item ID:"), tmpModifiers[i].modifierItemIdx, itemTypes);
    tmpModifiers[i].modifierItemID = itemTypes[tmpModifiers[i].modifierItemIdx];

    // 修改自定义列表中的值
    var key1Kv = tmpModifiers[i].GetOrAddMultiplier("Key1");
    key1Kv.value = EditorGUILayout.FloatField(new GUIContent("Key 1:"), key1Kv.value);

    // 更多字段修改...
}

Modifiers = tmpModifiers.ToArray();

// 标记对象为脏,触发保存
EditorUtility.SetDirty(target);
// 若操作的是预制体实例,还需记录预制体修改
PrefabUtility.RecordPrefabInstancePropertyModifications(target);

关键注意点

  • 永远不要依赖Unity序列化Dictionary,官方文档明确说明原生序列化不支持该类型。
  • 使用SerializedProperty是最稳妥的方式,能避免大部分序列化相关的问题。
  • 手动修改字段后必须调用EditorUtility.SetDirty,预制体场景还需额外调用PrefabUtility.RecordPrefabInstancePropertyModifications。

内容的提问来源于stack exchange,提问作者CHP

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 18:05:55