c# - 如何在 Inspector 编辑器脚本中绘制列表及其所有项目?
问题描述
主脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DialogueTrigger : MonoBehaviour
{
public List<Dialogue> dialogue = new List<Dialogue>();
[HideInInspector]
public int dialogueNum = 0;
private bool triggered = false;
public void TriggerDialogue()
{
if (triggered == false)
{
if (FindObjectOfType<DialogueManager>() != null)
{
FindObjectOfType<DialogueManager>().StartDialogue(dialogue[dialogueNum]);
dialogueNum += 1;
}
triggered = true;
}
}
private void Update()
{
if (DialogueManager.dialogueEnded == true)
{
if (dialogueNum == dialogue.Count)
{
return;
}
else
{
FindObjectOfType<DialogueManager>().StartDialogue(dialogue[dialogueNum]);
DialogueManager.dialogueEnded = false;
dialogueNum += 1;
}
}
}
}
创建项目名称和句子的对话脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class Dialogue
{
public string name;
[TextArea(1, 10)]
public string[] sentences;
}
和编辑器脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(DialogueTrigger))]
public class DialogueTriggerEditor : Editor
{
private SerializedProperty _dialogues;
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
_dialogues = serializedObject.FindProperty("dialogue");
serializedObject.Update();
for (int i = 0; i < _dialogues.arraySize; i++)
{
var dialogue = _dialogues.GetArrayElementAtIndex(i);
EditorGUILayout.PropertyField(dialogue, new GUIContent("Dialogue " + i));
}
}
}
但是现在我在 Inspector 中有一个对话变量,我可以设置对话的数量以及每个对话的名称和句子。
但在它下面,它会根据我设置的对话数量创建更多对话。
我希望在 Inspector 中拥有的是一个主要对话:
然后在里面我可以设置对话的数量。例如,如果我设置 5,那么在 Dialogues 下会有: Dialogue 1 Dialogue 2 Dialogue 3 Dialogue 4 Dialogue 5
然后在每个对话下面,例如对话 1,会有它的名称和句子。能够改变每个对话的句子大小。
解决方案
问题是EditorGUILayout.PropertyField
默认情况下不支持嵌套属性。
最简单的解决方法是使用正确的重载PropertyField(SerializedProperty property, GUIContent label, bool includeChildren, params GUILayoutOption[] options);
这需要bool includeChildren
:
[CustomEditor(typeof(DialogueTrigger))]
public class DialogueTriggerEditor : Editor
{
private SerializedProperty _dialogues;
private void OnEnable()
{
// do this only once here
_dialogues = serializedObject.FindProperty("dialogue");
}
public override void OnInspectorGUI()
{
//base.OnInspectorGUI();
serializedObject.Update();
// Ofcourse you also want to change the list size here
_dialogues.arraySize = EditorGUILayout.IntField("Size", _dialogues.arraySize);
for (int i = 0; i < _dialogues.arraySize; i++)
{
var dialogue = _dialogues.GetArrayElementAtIndex(i);
EditorGUILayout.PropertyField(dialogue, new GUIContent("Dialogue " + i), true);
}
// Note: You also forgot to add this
serializedObject.ApplyModifiedProperties();
}
}
请注意,还有其他更可定制的解决方案。另一个快速的方法可能是手动获取这些嵌套属性并定义它们应该如何绘制:
[CustomEditor(typeof(DialogueTrigger))]
public class DialogueTriggerEditor : Editor
{
private SerializedProperty _dialogues;
// store which dialogue is foldout
private List<bool> dialogueFoldout = new List<bool>();
private void OnEnable()
{
_dialogues = serializedObject.FindProperty("dialogue");
for (var i = 0; i < _dialogues.arraySize; i++)
{
dialogueFoldout.Add(false);
}
}
public override void OnInspectorGUI()
{
//base.OnInspectorGUI();
serializedObject.Update();
var color = GUI.color;
EditorGUI.BeginChangeCheck();
_dialogues.arraySize = EditorGUILayout.IntField("Size", _dialogues.arraySize);
if (EditorGUI.EndChangeCheck())
{
dialogueFoldout.Clear();
for (var i = 0; i < _dialogues.arraySize; i++)
{
dialogueFoldout.Add(false);
}
serializedObject.ApplyModifiedProperties();
return;
}
for (var i = 0; i < _dialogues.arraySize; i++)
{
var dialogue = _dialogues.GetArrayElementAtIndex(i);
dialogueFoldout[i] = EditorGUILayout.Foldout(dialogueFoldout[i], "Dialogue " + i);
// make the next fields look nested below the before one
EditorGUI.indentLevel++;
if (dialogueFoldout[i])
{
var name = dialogue.FindPropertyRelative("name");
var sentences = dialogue.FindPropertyRelative("sentences");
if (string.IsNullOrWhiteSpace(name.stringValue)) GUI.color = Color.yellow;
EditorGUILayout.PropertyField(name);
GUI.color = color;
// if you still want to be able to controll the size
sentences.arraySize = EditorGUILayout.IntField("Senteces size", sentences.arraySize);
// make the next fields look nested below the before one
EditorGUI.indentLevel++;
for (var s = 0; s < sentences.arraySize; s++)
{
var sentence = sentences.GetArrayElementAtIndex(s);
if (string.IsNullOrWhiteSpace(sentence.stringValue)) GUI.color = Color.yellow;
EditorGUILayout.PropertyField(sentence, new GUIContent("Sentece " + s));
GUI.color = color;
}
EditorGUI.indentLevel--;
}
EditorGUI.indentLevel--;
}
serializedObject.ApplyModifiedProperties();
}
}
您可以再次向前迈出一步,而是为您的班级使用完整的CustomPropertyDrawer 。Dialogue
这样做的巨大优势在于,不仅在这一类中DialogueTrigger
,而且在您拥有字段的任何地方都将public Dialogue
使用自定义抽屉来显示!
或者,如果您真的想要花哨的列表(可重新排序),可以简单地删除任何索引处的元素等,我强烈建议您查看ReorderableList。这是 Unity 在UnityEvent
(like onClick
) 中使用的一个未记录的功能,而且使用起来有点复杂,但是一旦你得到它,它就真的很强大了!(在我的问题中,我们还解决了如何将其用于嵌套列表,例如您的情况。)
推荐阅读
- bash - bash比较文件中的2个单词列表
- flutter - 创建列表后,ListView.builder itemBuilder 函数在滚动时重新执行
- javascript - 访问被阻止的电影的视频流
- oracle - 根据总分和成绩计算总贡献
- c# - 如何加快 Raven DB 查询性能?
- python - 在python中创建乘法文件
- d3.js - d3.js 图例:组之间的水平间距
- python - 如何方便地将额外信息与 Enum 成员相关联?
- android-studio - Android模拟器没有运行但也没有崩溃
- spring - Spring:如何在并行通量上附加 SecurityContextHolder