【问题标题】:Is there a way to use serializedObject in EditorWindow script?有没有办法在 EditorWindow 脚本中使用 serializedObject?
【发布时间】:2019-05-14 04:01:09
【问题描述】:

我有一个带有列表的脚本:

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;

public class Test : MonoBehaviour
{
    public List<Conversation> conversations = new List<Conversation>();

当只使用编辑器脚本而不是 EditorWindow 时,我可以这样做:

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;

[CustomEditor(typeof(Test))]
public class TestTriggerEditor : Editor
{
    private SerializedProperty _conversations;

    private void OnEnable()
    {
        _conversations = serializedObject.FindProperty("conversations");
    }

但现在我想在 EditorWindow 脚本中执行此操作:

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;

public class TestingEditorWindow : EditorWindow
{
    private SerializedProperty _conversations;

    [MenuItem("Testing/Editor")]
    private static void ConversationsSystem()
    {
        const int width = 340;
        const int height = 420;

        var x = (Screen.currentResolution.width - width) / 2;
        var y = (Screen.currentResolution.height - height) / 2;

        GetWindow<TestingEditorWindow>().position = new Rect(x, y, width, height);
    }

    private void OnGUI()
    {
        _conversations = serializedObject.FindProperty("conversations");
    }
}

但我在 OnGUI 内的行上出现错误:

_conversations = serializedObject.FindProperty("conversations");

错误出现在 serializedObject 上:

当前上下文中不存在名称“serializedObject”

如果我将其更改为 SerializedObject,则会出现错误:

非静态字段、方法或属性“SerializedObject.FindProperty(string)”需要对象引用

我尝试将 _conversations 更改为静态,但没有帮助。

我也尝试过:

SerializedObject serializedObject = new SerializedObject(

但不确定如何使用它。为什么在 Editor 脚本中它工作正常,但在 EditorWindow 中却不行?

【问题讨论】:

  • @RetiredNinja 首先EditorWindow 继承自ScriptableObject,后者已经可序列化的。其次,这并没有改变 EditorWindow 根本没有这样的属性的事实。原因见我的answer

标签: c# unity3d


【解决方案1】:

不同的是Editor

  1. 如您所见,它显然与某种特定类型有关

    [CustomEditor(typeof(Test))]
    
  2. 更重要的是,它是某个特定实例的检查器,该类型附加到当前选定的游戏对象。因此,您可以访问该MonoBehaviour(或ScriptableObject)的serializedObject 实例

    Editor.serializedObject

    SerializedObject 表示正在检查的一个或多个对象。


另一方面,EditorWindow与某个类型也不与某个实例或游戏对象相关,而只是一个将要打开的窗口。你可以说它存在于一种static 环境中(而不是像Editor 这样的实例化环境)。

因此根本没有 serializedObject 它会与 => 它没有这样的属性。


当然,使用SerializedObject 是行不通的,因为这是一个类型而不是实例,并且FindProperty 方法不是静态的,而是需要一个实例,因为错误消息试图告诉你。

我不想听起来很糟糕,但如果这是你还不理解的东西,那么你应该立即停止担心 EditorScripting,并在基本的 c# 编码和/或一般的面向对象编程方面获得一些更新。


可以使用constructor

var serializedObject = new SerializedObject(referenceToTest);

如果您可以通过某种方式获得对Test 的某个实例的引用。

你可以这样做,例如使用FindObjectOfType

var referenceToTest = FindObjectOfType<Test>();
if(! referenceToTest) return;

从当前场景中获取第一个活动并启用的Test 实例。

或者例如使用Selection.transforms

var selected = Selection.transforms;
Test referenceToTest;
foreach (var item in selected)
{
    referenceToTest = item.GetComponent<Test>();
    if(referenceToTest) break;
}

if(! referenceToTest) return;

从场景层级窗口中的选定对象中获取Test的第一次相遇。


作为替代方案,您可以从 Test 的检查器打开 EditorWindow,而不是从 static 菜单栏中打开它并传入 MonoBehaviour 引用。 (只有在 Inspector 保持打开状态时直接传递 serializedObject 才有效,因为当不再选择 GameObject 时,serializedObject 实例可能与 TestEditor 一起被销毁)

[CustomEditor(typeof(Test))]
public class TestEditor : Editor
{
    public override void OnInpectorGUI ()
    {
        // Draw the default inspector
        DrawDefaultInspector();

        EditorGUILayout.Space();

        // Add a button for opening the EditorWindow and pass in the reference
        if(GUILayout.Button("Open EditorWindow"))
        {
            TestEditorWindow.ConversationsSystem((Test) target);
        }
    }
}

并接收并存储参考

public class TestingEditorWindow : EditorWindow
{
    const int width = 340;
    const int height = 420;

    private SerializedProperty _conversations;

    private Test currentTestInstance;

    public static void ConversationsSystem(Test testInstance)
    { 
        var x = (Screen.currentResolution.width - width) / 2;
        var y = (Screen.currentResolution.height - height) / 2;

        var window = GetWindow<TestingEditorWindow>();

        window.position = new Rect(x, y, width, height);

        window.currentTestInstance = testInstance;
    }

    private void OnGUI()
    {
        if(! currentTestInstance)
        {
            EditorGUILayout.HelpBox("No Test instance selected!", MessageType.Error);
            return;
        }

        var serializedObject = new SerializedObject(currentTestInstance);

        _conversations = serializedObject.FindProperty("conversations");
    }
}

注意:在智能手机上打字,所以没有保修,但我希望这个想法能清楚

【讨论】:

    【解决方案2】:

    遇到了同样的问题。我没想到下面会起作用,但它确实起作用了。

    public class TestingEditorWindow : EditorWindow {
      [SerializeField]
      private string exampleField;
    
      [SerializeField]
      private List<int> exampleField2;
    
      private SerializedObject serializedObject;
    
      private void OnEnable() {
        serializedObject = new SerializedObject(this);
      }
    
      private void OnGUI() {
        EditorGUILayout.PropertyField(serializedObject.FindProperty("exampleField");
        EditorGUILayout.PropertyField(serializedObject.FindProperty("exampleField2");
        serializedObject.ApplyModifiedProperties();
      }
    
      [MenuItem("Testing/Editor")]
      private static void OpenEditorWindow() {
        GetWindow<TestingEditorWindow>();
      }
    }
    

    神奇之处在于允许创建EditorWindowSerializedObject,并且其工作方式与您期望它对普通MonoBehaviour 脚本的工作方式一样。但鉴于SerializedObject 实际上继承自ScriptableObject,我想这并不奇怪。

    【讨论】:

      猜你喜欢
      • 2020-07-31
      • 1970-01-01
      • 1970-01-01
      • 2016-03-18
      • 2017-04-29
      • 1970-01-01
      • 2023-01-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多