0

I am trying to create a Custom EditorWindow (TransporterToolKit.cs) with buttons of my game's fast-travel points.

I have a GameObject (TransporterSystem.cs, a singleton, the manager):

enter image description here

enter image description here

that has a LIST of child GameObjects that are Nodes (The GameObjects fast travel points). Each node has a Serializable TransporterLocation that holds details about the actual location.

I get null object error:

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

whether I am running the game or not using my current TransporterToolKit.cs file

How do I access the list of nodes so I can get their Serializable TransporterLocation?

EDITOW WINDOW:

What my problem question is about.

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
        }

    }
}

The other classes.

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;
    }

}
2
  • Can you identify & comment the line of code the null reference exception is coming from? Commented Nov 8, 2017 at 17:26
  • @Foggzie updated. Its in the TransporterToolKit.cs file Commented Nov 8, 2017 at 18:27

1 Answer 1

1

It looks like s_Instance is null. The problem is, that you are asking for the TransporterSystem static instance in OnEnable of the EditorWindow, however, the static instance will only be set in Awake during play mode. (At least this is what I assume after testing your code in my project).

I would fix this by actively searching for the TransporterSystem from within the editor window:

TransporterToolKit.cs

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

Alternatively, you could also make your singleton implementation lazy, similar to this:

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;
}

To fix the problem, that the Node is only updated in play mode:

// 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;
}

Instead of assigning the values in Awake, you need to do it at edit time. The simplest example would be via the Reset callback (but you could trigger it from your editor window or a special button as well). The important part is, that you want to not only set the values, but actually serialize the data to disk. This means, marking the scene as dirty. Unity recommends, to use the Undo class, to not only record an undoable action, but also set the scene as dirty. Alternatively, you can just do:

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

Beware, that the editor code needs to be either within a file that is placed in the Editor folder or surrounded by the compilation symbols UNITY_EDITOR.

Sign up to request clarification or add additional context in comments.

3 Comments

Wow that's really close! The buttons show up, but I failed to mention the transporter node's ThisLcocation is Vector3.zero until GamePlay, where they then get their current Vector3 location. Since I have the Node (The GameObject) how can I pull the Nodes transform and assign it to ThisLocation property? Either in GamePlayer or in Edit mode. I tried to just take the node.ThisLocation.transporterLocation = node.transform.position; but I get an error the object was destroyed. @Xarbrough
See my update and try if that fixes it (typed out of my head). Basically, you need to find a way of setting the values during edit more, making sure they are actually serialized to disk (dirty flag set).
Thank you so much! This has been a great help!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.