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

Unity中SerializedProperty访问子对象成员时出现空引用异常

Unity自定义编辑器ScriptableObject字段访问空引用问题

问题现象

开发自定义编辑器时,通过FindPropertyRelative获取到TalkerCharacter类型的SerializedProperty对象后,尝试访问其内部name成员变量时触发NullReferenceException。尽管character对应的SerializedProperty本身不为空,但调用prop.FindPropertyRelative("name").ToString()时抛出异常。

核心原因

TalkerCharacter是继承自ScriptableObject的引用类型,Unity序列化系统对这类对象的处理逻辑和值类型完全不同:

  • 直接用new TalkerCharacter()初始化的实例没有被Unity序列化系统正确注册,编辑器的SerializedProperty无法追踪其内部字段。
  • SerializedProperty对于ScriptableObject类型仅存储对象引用,而非直接包含其内部字段,因此不能通过FindPropertyRelative直接访问成员。

解决方案

1. 正确初始化ScriptableObject实例

在TalkerScript中,禁止用new创建TalkerCharacter,改用Unity官方提供的ScriptableObject.CreateInstance<T>()方法:

[System.Serializable]
public class TalkerScript
{
    public string text = "123";
    [Space(30)]
    public TalkerCharacter character;
    public string face = "";

    public TalkerScript(TalkerCharacter chr, string txt)
    {
        this.character = chr;
        this.text = txt;
    }

    // 无参构造中使用正确方式创建实例
    public TalkerScript()
    {
        character = ScriptableObject.CreateInstance<TalkerCharacter>();
    }
}

2. 编辑器中正确访问ScriptableObject内部字段

在自定义编辑器的drawElementCallback中,需先获取ScriptableObject实例,再创建对应的SerializedObject来访问内部字段:

scriptList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
{
    SerializedProperty element = scriptList.serializedProperty.GetArrayElementAtIndex(index);
    if (element != null)
    {
        rect.y += 2;
        SerializedProperty characterProp = element.FindPropertyRelative("character");
        
        // 先判断对象引用是否为空
        if (characterProp.objectReferenceValue != null)
        {
            // 创建对应ScriptableObject的SerializedObject
            SerializedObject characterSO = new SerializedObject(characterProp.objectReferenceValue);
            SerializedProperty nameProp = characterSO.FindProperty("name");
            
            if (nameProp != null)
            {
                Debug.Log(nameProp.stringValue);
                // 绘制name字段到编辑器
                EditorGUI.PropertyField(new Rect(rect.x, rect.y, 200, EditorGUIUtility.singleLineHeight), nameProp);
            }
            
            // 必须调用此方法保存修改
            characterSO.ApplyModifiedProperties();
        }

        EditorGUI.PropertyField(new Rect(rect.x, rect.y + EditorGUIUtility.singleLineHeight, 60, 60), element.FindPropertyRelative("face"), GUIContent.none);
        EditorGUI.TextArea(new Rect(rect.x + 60, rect.y + EditorGUIUtility.singleLineHeight, EditorGUIUtility.currentViewWidth - 260, 60), "12321");
    }
};

3. 额外注意事项

  • 如果TalkerCharacter是作为独立资源使用,建议通过Project面板右键创建实例(而非代码临时创建),确保资源被正确序列化存储。
  • 操作SerializedObject后必须调用ApplyModifiedProperties(),否则修改不会被保存到目标对象。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 00:12:23