gpt4 book ai didi

c# - 如何在检查器中使用带有列表的 ReorderableList 并添加新的空项目和折叠子项?

转载 作者:行者123 更新时间:2023-12-04 13:19:08 26 4
gpt4 key购买 nike

这就是我声明 List 的方式:

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>();

这是使用此 List 的编辑器脚本:
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 之前 Inspector 的屏幕截图。我可以点击一个对话项目并折叠它,然后在每个 child 上再次折叠它,依此类推:

Before using ReorderableList

此屏幕截图是使用 ReorderableList 时的截图:

When using ReorderableList

现在我只能拖动项目并更改位置,当单击 + 时,它将添加最后一个项目的副本。

我现在想做的事情我至少能想到三件事:
  • 选择现有项目时,例如 Opening,然后单击 + 在 Opening 下添加一个新的空对话项目。如果我选择了 Magic 在最后添加空的新项目,如果选择了中间 Locked Room 中的一个,在它后面添加空项目。这个想法是在所选项目之后添加空项目。
  • 删除与所选项目相同的项目时
  • 当单击其中一个项目或制作一个箭头来折叠项目并在我的问题(如 Opening 和其他人)中的屏幕截图中显示所有 child 时,我该如何做到这一点?
  • 最佳答案

    我花了很长时间,但我喜欢 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 s 在字典中:
    [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
    }

    添加新的对话,给它们命名,折叠它们。如果选择了对话,则在其后添加新对话

    enter image description here

    添加对话和句子,并且仍然能够重新排列和(展开)折叠所有内容

    enter image description here

    关于c# - 如何在检查器中使用带有列表的 ReorderableList 并添加新的空项目和折叠子项?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56180821/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com