这就是我声明列表的方式:
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEditorInternal;
public class ConversationTrigger : MonoBehaviour
{
public List<Conversation> conversations = new List<Conversation>();
这是使用此列表的编辑器脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
[CustomEditor(typeof(ConversationTrigger))]
public class ConversationTriggerEditor : Editor
{
private ReorderableList ConversationsList;
private void OnEnable()
{
ConversationTrigger conversationtrigger = (ConversationTrigger)target;
ConversationsList = new ReorderableList(serializedObject, serializedObject.FindProperty("conversations"), true, true, true, true);
}
public override void OnInspectorGUI()
{
serializedObject.Update();
ConversationsList.DoLayoutList();
serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(target);
}
}
这是使用ReorderableList之前检查器的屏幕截图。我可以单击“对话”项并将其折叠,然后在每个孩子上再次将其折叠,依此类推:
此屏幕截图是在使用ReorderableList时:
现在,我只能拖动项目并更改位置,单击+时将添加最后一项的重复项。
我现在想做什么,至少可以想到三件事:
在选择现有项目(例如“开幕”)时,如果单击+,则在“开幕”下添加一个新的空对话项。如果选择了“魔术”,则在末尾添加一个空的新项;如果选择了“中间的锁间”中的一个,则在其后添加一个空项。想法是在所选项目之后添加空项目。
当删除项目时,所选项目相同
当双击其中一项时,如何使箭头折叠以折叠并显示所有子项,如我的问题中的“ Opening”和“ Opening”等,该如何做到?
答案 0 :(得分:1)
花了我一段时间,但我喜欢EditorScripting:D
假设您的数据结构如下所示
public class ConversationTrigger : MonoBehaviour
{
public List<Conversation> conversations;
public void SaveConversations() { }
public void LoadConversations() { }
}
[Serializable]
public class Conversation
{
public string Name;
public bool Foldout;
public List<Dialogue> Dialogues;
}
[Serializable]
public class Dialogue
{
public string Name;
public bool Foldout;
public List<string> Sentences;
}
我想出了以下EditorScript。这个想法是基于我以前在How to select elements in nested reordeablelist上提出的问题。
与EditorScript一样,它具有相当的灵活性。我试图发表很多评论,但是如果不清楚,请在评论中问我。
非常遗憾的是ReorderableList
不是文档功能,因为它是如此强大和有用...
您必须重写多项内容:
drawHeaderCallback
drawElementCallback
elementHeightCallback
onAddCallback
并且为了能够与它们嵌套使用,在字典中存储了不同的ReorderableList
:
[CustomEditor(typeof(ConversationTrigger))]
public class ConversationTriggerEditor : Editor
{
private ConversationTrigger _conversationTrigger;
[SerializeField] private ReorderableList conversationsList;
private SerializedProperty _conversations;
private int _currentlySelectedConversationIndex = -1;
private readonly Dictionary<string, ReorderableList> _dialoguesListDict = new Dictionary<string, ReorderableList>();
private readonly Dictionary<string, ReorderableList> _sentencesListDict = new Dictionary<string, ReorderableList>();
private void OnEnable()
{
_conversationTrigger = (ConversationTrigger)target;
_conversations = serializedObject.FindProperty("conversations");
conversationsList = new ReorderableList(serializedObject, _conversations)
{
displayAdd = true,
displayRemove = true,
draggable = true,
drawHeaderCallback = DrawConversationsHeader,
drawElementCallback = DrawConversationsElement,
onAddCallback = (list) =>
{
SerializedProperty addedElement;
// if something is selected add after that element otherwise on the end
if (_currentlySelectedConversationIndex >= 0)
{
list.serializedProperty.InsertArrayElementAtIndex(_currentlySelectedConversationIndex + 1);
addedElement = list.serializedProperty.GetArrayElementAtIndex(_currentlySelectedConversationIndex + 1);
}
else
{
list.serializedProperty.arraySize++;
addedElement = list.serializedProperty.GetArrayElementAtIndex(list.serializedProperty.arraySize - 1);
}
var name = addedElement.FindPropertyRelative("Name");
var foldout = addedElement.FindPropertyRelative("Foldout");
var dialogues = addedElement.FindPropertyRelative("Dialogues");
name.stringValue = "";
foldout.boolValue = true;
dialogues.arraySize = 0;
},
elementHeightCallback = (index) =>
{
return GetConversationHeight(_conversations.GetArrayElementAtIndex(index));
}
};
}
public override void OnInspectorGUI()
{
serializedObject.Update();
// if there are no elements reset _currentlySelectedConversationIndex
if (conversationsList.serializedProperty.arraySize - 1 < _currentlySelectedConversationIndex) _currentlySelectedConversationIndex = -1;
conversationsList.DoLayoutList();
if (GUILayout.Button("Save Conversations"))
{
_conversationTrigger.SaveConversations();
}
if (GUILayout.Button("Load Conversations"))
{
Undo.RecordObject(_conversationTrigger, "Loaded conversations from JSON");
_conversationTrigger.LoadConversations();
}
serializedObject.ApplyModifiedProperties();
}
#region Drawers
#region List Headers
private void DrawConversationsHeader(Rect rect)
{
EditorGUI.LabelField(rect, "Conversations");
}
private void DrawDialoguesHeader(Rect rect)
{
EditorGUI.LabelField(rect, "Dialogues");
}
private void DrawSentencesHeader(Rect rect)
{
EditorGUI.LabelField(rect, "Sentences");
}
#endregion List Headers
#region Elements
private void DrawConversationsElement(Rect rect, int index, bool isActive, bool isFocused)
{
if (isActive) _currentlySelectedConversationIndex = index;
var conversation = _conversations.GetArrayElementAtIndex(index);
var position = new Rect(rect);
var name = conversation.FindPropertyRelative("Name");
var foldout = conversation.FindPropertyRelative("Foldout");
var dialogues = conversation.FindPropertyRelative("Dialogues");
string dialoguesListKey = conversation.propertyPath;
EditorGUI.indentLevel++;
{
// make the label be a foldout
foldout.boolValue = EditorGUI.Foldout(new Rect(position.x, position.y, 10, EditorGUIUtility.singleLineHeight), foldout.boolValue, foldout.boolValue ? "" : name.stringValue);
if (foldout.boolValue)
{
// draw the name field
name.stringValue = EditorGUI.TextField(new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight), name.stringValue);
position.y += EditorGUIUtility.singleLineHeight;
if (!_dialoguesListDict.ContainsKey(dialoguesListKey))
{
// create reorderabl list and store it in dict
var dialoguesList = new ReorderableList(conversation.serializedObject, dialogues)
{
displayAdd = true,
displayRemove = true,
draggable = true,
drawHeaderCallback = DrawDialoguesHeader,
drawElementCallback = (convRect, convIndex, convActive, convFocused) => { DrawDialoguesElement(_dialoguesListDict[dialoguesListKey], convRect, convIndex, convActive, convFocused); },
elementHeightCallback = (dialogIndex) =>
{
return GetDialogueHeight(_dialoguesListDict[dialoguesListKey].serializedProperty.GetArrayElementAtIndex(dialogIndex));
},
onAddCallback = (list) =>
{
list.serializedProperty.arraySize++;
var addedElement = list.serializedProperty.GetArrayElementAtIndex(list.serializedProperty.arraySize - 1);
var newDialoguesName = addedElement.FindPropertyRelative("Name");
var newDialoguesFoldout = addedElement.FindPropertyRelative("Foldout");
var sentences = addedElement.FindPropertyRelative("Sentences");
newDialoguesName.stringValue = "";
newDialoguesFoldout.boolValue = true;
sentences.arraySize = 0;
}
};
_dialoguesListDict[dialoguesListKey] = dialoguesList;
}
_dialoguesListDict[dialoguesListKey].DoList(new Rect(position.x, position.y, position.width, position.height - EditorGUIUtility.singleLineHeight));
}
}
EditorGUI.indentLevel--;
}
private void DrawDialoguesElement(ReorderableList list, Rect rect, int index, bool isActive, bool isFocused)
{
if (list == null) return;
var dialog = list.serializedProperty.GetArrayElementAtIndex(index);
var position = new Rect(rect);
var foldout = dialog.FindPropertyRelative("Foldout");
var name = dialog.FindPropertyRelative("Name");
{
// make the label be a foldout
foldout.boolValue = EditorGUI.Foldout(new Rect(position.x, position.y, 10, EditorGUIUtility.singleLineHeight), foldout.boolValue, foldout.boolValue ? "" : name.stringValue);
var sentencesListKey = dialog.propertyPath;
var sentences = dialog.FindPropertyRelative("Sentences");
if (foldout.boolValue)
{
// draw the name field
name.stringValue = EditorGUI.TextField(new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight), name.stringValue);
position.y += EditorGUIUtility.singleLineHeight;
if (!_sentencesListDict.ContainsKey(sentencesListKey))
{
// create reorderabl list and store it in dict
var sentencesList = new ReorderableList(sentences.serializedObject, sentences)
{
displayAdd = true,
displayRemove = true,
draggable = true,
// header for the dialog list
drawHeaderCallback = DrawSentencesHeader,
// how a sentence is displayed
drawElementCallback = (sentenceRect, sentenceIndex, sentenceIsActive, sentenceIsFocused) =>
{
var sentence = sentences.GetArrayElementAtIndex(sentenceIndex);
// draw simple textArea for sentence
sentence.stringValue = EditorGUI.TextArea(sentenceRect, sentence.stringValue);
},
// Sentences have simply a fixed height of 2 lines
elementHeight = EditorGUIUtility.singleLineHeight * 2,
// when a sentence is added
onAddCallback = (sentList) =>
{
sentList.serializedProperty.arraySize++;
var addedElement = sentList.serializedProperty.GetArrayElementAtIndex(sentList.serializedProperty.arraySize - 1);
addedElement.stringValue = "";
}
};
// store the created ReorderableList
_sentencesListDict[sentencesListKey] = sentencesList;
}
// Draw the list
_sentencesListDict[sentencesListKey].DoList(new Rect(position.x, position.y, position.width, position.height - EditorGUIUtility.singleLineHeight));
}
}
}
#endregion Elements
#endregion Drawers
#region Helpers
#region HeightGetter
/// <summary>
/// Returns the height of given Conversation property
/// </summary>
/// <param name="conversation"></param>
/// <returns>height of given Conversation property</returns>
private float GetConversationHeight(SerializedProperty conversation)
{
var foldout = conversation.FindPropertyRelative("Foldout");
// if not foldout the height is simply 1 line
var height = EditorGUIUtility.singleLineHeight;
// otherwise we sum up every controls and child heights
if (foldout.boolValue)
{
// we need some more lines:
// for the Name field,
// the list header,
// the list buttons and a bit buffer
height += EditorGUIUtility.singleLineHeight * 5;
var dialogues = conversation.FindPropertyRelative("Dialogues");
for (var d = 0; d < dialogues.arraySize; d++)
{
var dialog = dialogues.GetArrayElementAtIndex(d);
height += GetDialogueHeight(dialog);
}
}
return height;
}
/// <summary>
/// Returns the height of given Dialogue property
/// </summary>
/// <param name="dialog"></param>
/// <returns>height of given Dialogue property</returns>
private float GetDialogueHeight(SerializedProperty dialog)
{
var foldout = dialog.FindPropertyRelative("Foldout");
// same game for the dialog if not foldout it is only a single line
var height = EditorGUIUtility.singleLineHeight;
// otherwise sum up controls and child heights
if (foldout.boolValue)
{
// we need some more lines:
// for the Name field,
// the list header,
// the list buttons and a bit buffer
height += EditorGUIUtility.singleLineHeight * 4;
var sentences = dialog.FindPropertyRelative("Sentences");
// the sentences are easier since they always have the same height
// in this example 2 lines so simply do
// at least have space for 1 sentences even if there is none
height += EditorGUIUtility.singleLineHeight * Mathf.Max(1, sentences.arraySize) * 2;
}
return height;
}
#endregion
#endregion Helpers
}
添加新的对话,为其命名,然后对其进行折叠。如果选择了对话,请在其后添加新对话
添加对话和句子,仍然能够重新排列和(展开)所有内容