英文:
(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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论