在自定义 EditorWindow 中保存字符串变量,同时格式化为 TextArea

Saving string variables in a custom EditorWindow while formatted like a TextArea

基本上,我想弄清楚我该怎么做:

  1. 在自定义编辑器中保存字符串(或任何)变量window(继承 来自 EditorWindow) 当它们在 window.
  2. 中被更改时
  3. 以类似 TextArea 的格式显示字符串,同时仍然允许 如上所述保存更改。
  4. 按索引显示字符串数组中的字符串,而不是单独定义的字符串(我以前遇到过这个问题)
  5. 如果您也知道如何在自定义检查器中执行上述操作 (继承自 Editor,而非 EditorWindow),那也很棒。

我已经 运行 解决了这个问题几次,不同的 类 继承自编辑器,并且之前使用 PropertyField 而不是 TextArea/TextField 解决了这个问题,但现在已经解决了我想要的 TextArea 样式格式。 此外,从 EditorWindow 继承的 类 似乎不允许以相同的方式使用它(t = (script type)target; 不起作用,而 PropertyField 需要它)..?

我对自定义检查器和这些东西还很陌生,所以如果可能的话,代码示例会非常有用。

谢谢!

在开始一般性说明之前,因为您在问题中提到了它:

我强烈建议尽可能避免使用 target!特别是不要直接设置任何字段。这使得诸如标记您的场景 direty 并因此保存更改和 Undo/Redo 功能变得非常复杂,因为您必须自己实现它!

而总是通过 SerializedProperty combined with SerializedObject.Update and SerializedObject.ApplyModifiedProperties(示例将在下面)。这会处理所有这些东西,例如标记 dirty,从而自动为您保存场景更改和 Undo/Redo!


然后到TextArea。

假设您有一个 class 喜欢

public class Example : MonoBehaviour
{
    [SerializeField] private string _exampleString;
    public string AnotherExampleString;
}

基本上有三个主要选项。我将首先执行 Editor(自定义检查器)脚本,因为您可以更灵活一些。

下面是EditorWindow


编辑器属性 [TextArea]

实际上您根本不需要 Editor 脚本!只需将相应字段标记为 [TextArea],如下所示:

public class Example : MonoBehaviour
{
    [SerializeField] [TextArea] private string _exampleString;
    // You can also directly configure the min and max line count here as well
    // By default it is 3 lines
    [TextAre(3,7)] public string AnotherExampleString;
}

这看起来像这样


EditorGUILayout.PropertyField

然后 如果 你仍然需要 Editor 脚本 EditorGUILayout.PropertyField 的好处是它会自动为相应的类型使用正确的抽屉.. . 而且它 还应用所有编辑器属性 !这不是很好吗?

所以简单地拥有和Editor喜欢

[CustomEditor(typeof(Example))]
public class ExampleEditor : Editor
{
    private SerializedProperty _exampleString;
    private SerializedProperty AnotherExampleString;

    private void OnEnable()
    {
        // Link in the serialized properties to their according fields
        _exampleString = serializedObject.FindProperty("_exampleString");
        AnotherExampleString = serializedObject.FindProperty("AnotherExampleString");
    }

    public override void OnInspectorGUI()
    {
        DrawScriptField();

        // load the real target values into the serialized properties
        serializedObject.Update();

        EditorGUILayout.PropertyField(_exampleString);
        EditorGUILayout.PropertyField(AnotherExampleString);

        // write back the changed properties into the real target
        serializedObject.ApplyModifiedProperties();
    }

    // Little bonus from my side so you have the script field on top
    private void DrawScriptField()
    {
        EditorGUI.BeginDisabledGroup(true);
        EditorGUILayout.ObjectField("Script", MonoScript.FromMonoBehaviour((Example)target), typeof(Example), false);
        EditorGUILayout.Space();
        EditorGUI.EndDisabledGroup();
    }
}

结果看起来基本完全一样:

EditorGUILayout.TextField

使用 EditorGUILayout.TextArea 您可以将 any string 显示为多行文本区域。这也适用于 EditorWindow.

再说一遍,我们没有标记 string 个字段

public class Example : MonoBehaviour
{
    [SerializeField] private string _exampleString;
    public string AnotherExampleString;
}

但是我们可以使用这个 Editor 脚本使它们像以前一样显示:

[CustomEditor(typeof(Example))]
public class ExampleEditor : Editor
{
    private SerializedProperty _exampleString;
    private SerializedProperty AnotherExampleString;

    private Vector2 scroll1;
    private Vector2 scroll2;

    private void OnEnable()
    {
        // Link in the serialized properties to their according fields
        _exampleString = serializedObject.FindProperty("_exampleString");
        AnotherExampleString = serializedObject.FindProperty("AnotherExampleString");
    }

    public override void OnInspectorGUI()
    {
        DrawScriptField();

        // load the real target values into the serialized properties
        serializedObject.Update();

        EditorGUILayout.PrefixLabel(_exampleString.displayName);
        scroll1 = EditorGUILayout.BeginScrollView(scroll1,GUILayout.MaxHeight(3 * EditorGUIUtility.singleLineHeight));
        _exampleString.stringValue = EditorGUILayout.TextArea(_exampleString.stringValue, EditorStyles.textArea);
        EditorGUILayout.EndScrollView();

        EditorGUILayout.PrefixLabel(AnotherExampleString.displayName);
        scroll2 = EditorGUILayout.BeginScrollView(scroll2, GUILayout.MaxHeight(7 * EditorGUIUtility.singleLineHeight));
        AnotherExampleString.stringValue = EditorGUILayout.TextArea(AnotherExampleString.stringValue);
        EditorGUILayout.EndScrollView();

        // write back the changed properties into the real target
        serializedObject.ApplyModifiedProperties();
    }

    // Little bonus from my side so you have the script field on top
    private void DrawScriptField()
    {
        EditorGUI.BeginDisabledGroup(true);
        EditorGUILayout.ObjectField("Script", MonoScript.FromMonoBehaviour((Example)target), typeof(Example), false);
        EditorGUILayout.Space();
        EditorGUI.EndDisabledGroup();
    }
}

虽然你可以看到我们已经不得不使用额外的 EditorGUILayout.BeginScrollView

来伪造它了

您也可以在 EditorWindow 中执行同样的操作。大多数时候,EditorWindow

通过 SerializedProperty 没有多大意义
public class ExampleWindow : EditorWindow
{
    private string exampleString;
    private Vector2 scroll;

    [MenuItem("Example/Show ExampleWindow")]
    private static void Initialize()
    {
        var window = GetWindow<ExampleWindow>();
        window.Show();
    }

    private void OnGUI()
    {
        EditorGUILayout.PrefixLabel("Example String");
        scroll = EditorGUILayout.BeginScrollView(scroll,GUILayout.MaxHeight(3 * EditorGUIUtility.singleLineHeight));
        exampleString = EditorGUILayout.TextArea(exampleString, EditorStyles.textArea);
        EditorGUILayout.EndScrollView();
    }
}

这导致