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

时间:2017-11-08 16:27:00

标签: c# unity3d unity5

我正在尝试使用游戏快速旅行点的按钮创建自定义EditorWindow TransporterToolKit.cs )。

我有一个GameObject TransporterSystem.cs ,一个单身人士,经理):

enter image description here

enter image description here

具有 LIST 的子游戏对象是节点(GameObjects快速旅行点)。每个节点都有一个 Serializable TransporterLocation ,其中包含有关实际位置的详细信息。

我得到空对象错误:

  

NullReferenceException:未将对象引用设置为的实例   对象

我是否正在使用我当前的 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;
    }

}

1 个答案:

答案 0 :(得分:1)

看起来s_Instance为null。问题是,您要求在EditorWindow的OnEnable中使用TransporterSystem静态实例,但是,静态实例仅在播放模式下设置为Awake。 (至少这是我在项目中测试代码后的假设)。

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

<强> 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回调(但您可以从编辑器窗口或特殊按钮触发它)。重要的是,您不仅要设置值,还要将数据序列化到磁盘。这意味着,将场景标记为脏。 Unity建议,使用Undo类不仅可以记录可撤消的操作,还可以将场景设置为脏。或者,您可以这样做:

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

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