访问自定义 Unity 编辑器的序列化列表对象 Window

Access Serialized List Object for Custom Unity Editor Window

我正在尝试创建自定义 EditorWindowTransporterToolKit.cs) -旅行积分。

我有一个GameObject(TransporterSystem.cs,单身,经理:

有一个 LIST 子游戏对象,它们是节点(游戏对象快速移动点)。每个节点都有一个 Serializable TransporterLocation 来保存有关实际位置的详细信息。

我收到空对象错误:

NullReferenceException: Object reference not set to an instance of an object

我是否正在运行游戏中使用我当前的TransporterToolKit.cs文件

如何访问节点列表以便获取它们的 Serializable TransporterLocation?

EDITOW WINDOW:

我的问题是什么。

TransporterToolKit.cs

public class TransporterToolKit : EditorWindow {

    [MenuItem("Project ToolKits/Transporter ToolKit")] 
    public static void ShowWindow() {
        GetWindow<TransporterToolKit>("Transporter ToolKit"); 
    }

    public List<TransporterNode> nodes;

    private void OnEnable() {

//ERROR COMES FROM THIS LINE
        nodes = TransporterSystem.s_Instance.GetAllTransporterNodes();
    }

    private void OnGUI() {

        GUILayout.Label("Transporter System", EditorStyles.boldLabel);

//Create a list of buttons with the location name
        foreach (TransporterNode node in nodes) {
            EditorGUILayout.BeginVertical(EditorStyles.helpBox);
            GUILayout.BeginHorizontal();
            if (GUILayout.Button(node.ThisLocation.locationName)) {
                //Do something
            }
            GUILayout.EndHorizontal();
            EditorGUILayout.EndVertical();  //end section outline
        }

    }
}

另一个类.

TransporterSystem.cs

public class TransporterSystem : Singleton<TransporterSystem> {
    [Header("Transporter Nodes")]
    [SerializeField]
    private List<TransporterNode> nodeList;

    public List<TransporterNode> GetAllTransporterNodes() {
        return nodeList;
    }
}

TransporterNode.cs

public class TransporterNode : MonoBehaviour {

    [SerializeField]
    private TransporterLocation thisLocation;
    public TransporterLocation ThisLocation {
        get {
            return thisLocation;
        }

        set {
            thisLocation = value;
        }

    void Awake() {
        ThisLocation.locationName = gameObject.name;
        ThisLocation.transporterLocation = transform.position;
    }

}

TransporterNode.cs

public enum TRANSPORTER_NODE_STATE {
    OFFLINE,
    ONLINE
}

[Serializable]
public class TransporterLocation {

    public Vector3 transporterLocation;
    public TRANSPORTER_NODE_STATE locationState;
    public string locationName;

    public TransporterLocation() {
        transporterLocation = Vector3.zero;
        locationName = "NOT SET";
        locationState = TRANSPORTER_NODE_STATE.OFFLINE;
    }

}

看起来 s_Instance 为空。问题是,您在 EditorWindow 的 OnEnable 中请求 TransporterSystem 静态实例,但是,静态实例只会在播放模式期间设置为 Awake。 (至少这是我在我的项目中测试你的代码后的假设)。

我会通过在编辑器中主动搜索 TransporterSystem 来解决这个问题 window:

TransporterToolKit.cs

private void OnEnable()
{
    TransporterSystem system = FindObjectOfType<TransporterSystem>();
    if (system == null)
    {
        Debug.LogError("No TransporterSystem in scene.");
    }
    else
    {
        nodes = system.GetAllTransporterNodes();
    }
}

或者,您也可以让您的单例实现变得惰性,类似于:

public class MonoSingleton
{
    public static TransporterSystem instance
    {
        get
        {
            if (m_Instance == null)
                m_Instance = Object.FindObjectOfType<TransporterSystem>();

            if (m_Instance == null)
                Debug.LogError("Unable to find TransporterSystem instance in scene.");

            return m_Instance;
        }
    }

    private static TransporterSystem m_Instance;
}

要解决节点仅在播放模式下更新的问题:

// Reset is called when the component is added to a GameObject or when Reset is selected from the inspector.
void Reset() {
    #if UNITY_EDITOR
    UnityEditor.Undo.RecordObject(gameObject, "Update LocationNode");
    #endif
    ThisLocation.locationName = gameObject.name;
    ThisLocation.transporterLocation = transform.position;
}

您需要在编辑时分配值,而不是在 Awake 中分配值。最简单的示例是通过 Reset 回调(但您也可以从编辑器 window 或特殊按钮触发它)。重要的是,您不仅要设置值,还要将数据实际序列化到磁盘。这意味着,将场景标记为脏。 Unity推荐,使用Undo class,不仅可以记录一个不可撤销的动作,还可以将场景设置为dirty。或者,您可以这样做:

UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(gameObject.scene);

请注意,编辑器代码需要位于 Editor 文件夹中的文件中,或者被编译符号包围 UNITY_EDITOR.