问题出在SerializedProperty。在Unity中,FindPropertyRelative返回null。

huangapple go评论82阅读模式
英文:

(C#) Problem with SerializedProperty. FindPropertyRelative returning null in Unity

问题

在这段代码中,使用FindPropertyRelative找到的character变量的序列化版本在打印到Debug.Log时显示为非null,但当我尝试再次使用这个变量的FindPropertyRelative来访问其中的任何其他变量时,会发生错误。

英文:

I am struggling to create a custom editor in Unity.
However, I am currently experiencing a problem that I cannot access other member variables inside the TalkerCharacter type variable found through the FindPropertyRelative function.
Below is some of the code I wrote.
In the Debug.Log part, "NullReferenceException: Object reference not set to an instance of an object" error occurs.
I apologize for my weak English skills. Any help would be appreciated.

[System.Serializable]
public class TalkerMessage : ScriptableObject
{
    public TalkerScript[] scripts;
}

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

    public TalkerScript(TalkerCharacter chr, string txt)
    {
        this.character = chr;
        this.text = txt;
    }
}
[Serializable]
public class TalkerCharacter : ScriptableObject
{
    public new string name = "";
    public TalkerFace[] faces = new TalkerFace[10];
    //public enum FACE { Normal, Smile, Etc1, Etc2, Etc3, Etc4, Etc5 , Etc6 , Etc7 , Etc8 , Etc9, Etc10 }
    //public Tuple<string, Sprite>[] faces = new Tuple<string, Sprite>[20];
    //public Sprite[] illust = new Sprite[System.Enum.GetNames(typeof(FACE)).Length];
    //public string dict = "";
    public TalkerCharacter()
    {
        name = "";
        faces = new TalkerFace[10];
    }
}


[CustomEditor(typeof(TalkerMessage))]
[System.Serializable]
public class TalkerMessageEditor : Editor 
{
    private Vector2 scrollPos = Vector2.zero;
    [SerializeField]
    private TalkerCharacter character;
    TalkerMessage other;
    SerializedProperty m_script;
    private ReorderableList scriptList;
    
    void OnEnable()
    {
        other = target as TalkerMessage;
        m_script = serializedObject.FindProperty("scripts");
        scriptList = new ReorderableList(serializedObject, m_script, true, true, true, true);

        scriptList.elementHeight = 60 + EditorGUIUtility.singleLineHeight;
        scriptList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
        {
            SerializedProperty element = scriptList.serializedProperty.GetArrayElementAtIndex(index);
            if (element != null)
            {
                rect.y += 2;
                SerializedProperty prop = element.FindPropertyRelative("character");
                Debug.Log(prop.FindPropertyRelative("name").ToString());
                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");
            }
        };
    }
}

In this code, the serialized version of the character variable found with FindPropertyRelative shows up as non-null when printed to Debug.Log, but when I try to use FindPropertyRelative once more with this variable to access any other variable within it, an error occurs.

答案1

得分: 0

您进一步嵌套的TalkerCharacter元素都是ScriptableObject资产,而不是普通的序列化类,比如TalkerScript

=> 对于这些元素,prop 实际上只包含一个 objectReferenceValue

为了更深入地了解它,您需要再次创建一个临时的 SerailizedObject,例如:

SerializedProperty element = scriptList.serializedProperty.GetArrayElementAtIndex(index);

rect.y += 2;
var prop = element.FindPropertyRelative("character");
if (prop.objectReferenceValue)
{
    var so = new SerializedObject(prop.objectReferenceValue);   
    Debug.Log(so.FindProperty("name").stringValue);
    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");
}

总的来说,我建议避免隐藏或遮盖内置的 UnityEngine.Object.name 属性。而是使用不同的字段名,如 characterName或者根本不使用自定义字段,而只使用内置字段。它同时等于实际的资产名称,因此直接在ScriptableObject的名称上命名您的角色可能更方便。

英文:

Your further nested elements of TalkerCharacter are all ScriptableObject assets and not normal serialized classes such as the TalkerScript.

=> For those the prop actually only contains an objectReferenceValue.

In order to dive deeper into that one you need to create a temporary SerailizedObject of it again like e.g.

SerializedProperty element = scriptList.serializedProperty.GetArrayElementAtIndex(index);

rect.y += 2;
var prop = element.FindPropertyRelative("character");
if(prop.objectReferenceValue)
{
    var so = new SerializedObject(prop.objectReferenceValue);   
    Debug.Log(so.FindProperty("name").stringValue);
    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");
}

In general I would avoid to hide/shadow the built-in UnityEngine.Object.name property. Rather use a different field name like characterName or don't use a custom field at all but simply stick to the built-in one. It equals at the same time the actual asset name so actually it is quite handy to just stick to that and name your characters directly already on the ScriptableObject name itself.

huangapple
  • 本文由 发表于 2023年2月24日 16:18:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/75554103.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定