I cant pin down the Null Reference Bug

It has been working correctly previously and i only added the dialogueName in DialogueNode and added a Textfield for that.

Error Message:

NullReferenceException: Object reference not set to an instance of an object
RPG.Dialogue.Dialogue+<GetAllChildren>d__8.MoveNext () (at Assets/Scripts/Dialogue/Dialogue.cs:69)
RPG.Dialogue.Editor.DialogueEditor.DrawConnections (RPG.Dialogue.DialogueNode node) (at Assets/Scripts/Dialogue/Editor/DialogueEditor.cs:95)
RPG.Dialogue.Editor.DialogueEditor.OnGUI () (at Assets/Scripts/Dialogue/Editor/DialogueEditor.cs:64)
UnityEditor.HostView.InvokeOnGUI (UnityEngine.Rect onGUIPosition) (at <1f0be198f5164d2489de92f22c998266>:0)
UnityEditor.DockArea.DrawView (UnityEngine.Rect dockAreaRect) (at <1f0be198f5164d2489de92f22c998266>:0)
UnityEditor.DockArea.OldOnGUI () (at <1f0be198f5164d2489de92f22c998266>:0)
UnityEngine.UIElements.IMGUIContainer.DoOnGUI (UnityEngine.Event evt, UnityEngine.Matrix4x4 parentTransform, UnityEngine.Rect clippingRect, System.Boolean isComputingLayout, UnityEngine.Rect layoutSize, System.Action onGUIHandler, System.Boolean canAffectFocus) (at <f2e1435fec084ff693c649189d4dffd4>:0)
UnityEngine.UIElements.IMGUIContainer.HandleIMGUIEvent (UnityEngine.Event e, UnityEngine.Matrix4x4 worldTransform, UnityEngine.Rect clippingRect, System.Action onGUIHandler, System.Boolean canAffectFocus) (at <f2e1435fec084ff693c649189d4dffd4>:0)
UnityEngine.UIElements.IMGUIContainer.DoIMGUIRepaint () (at <f2e1435fec084ff693c649189d4dffd4>:0)
UnityEngine.UIElements.UIR.RenderChainCommand.ExecuteNonDrawMesh (UnityEngine.UIElements.UIR.DrawParams drawParams, System.Single pixelsPerPoint, System.Exception& immediateException) (at <f2e1435fec084ff693c649189d4dffd4>:0)
Rethrow as ImmediateModeException
UnityEngine.UIElements.UIR.RenderChain.Render () (at <f2e1435fec084ff693c649189d4dffd4>:0)
UnityEngine.UIElements.UIRRepaintUpdater.Update () (at <f2e1435fec084ff693c649189d4dffd4>:0)
UnityEngine.UIElements.VisualTreeUpdater.UpdateVisualTreePhase (UnityEngine.UIElements.VisualTreeUpdatePhase phase) (at <f2e1435fec084ff693c649189d4dffd4>:0)
UnityEngine.UIElements.Panel.UpdateForRepaint () (at <f2e1435fec084ff693c649189d4dffd4>:0)
UnityEngine.UIElements.Panel.Repaint (UnityEngine.Event e) (at <f2e1435fec084ff693c649189d4dffd4>:0)
UnityEngine.UIElements.UIElementsUtility.DoDispatch (UnityEngine.UIElements.BaseVisualElementPanel panel) (at <f2e1435fec084ff693c649189d4dffd4>:0)
UnityEngine.UIElements.UIElementsUtility.UnityEngine.UIElements.IUIElementsUtility.ProcessEvent (System.Int32 instanceID, System.IntPtr nativeEventPtr, System.Boolean& eventHandled) (at <f2e1435fec084ff693c649189d4dffd4>:0)
UnityEngine.UIElements.UIEventRegistration.ProcessEvent (System.Int32 instanceID, System.IntPtr nativeEventPtr) (at <f2e1435fec084ff693c649189d4dffd4>:0)
UnityEngine.UIElements.UIEventRegistration+<>c.<.cctor>b__1_2 (System.Int32 i, System.IntPtr ptr) (at <f2e1435fec084ff693c649189d4dffd4>:0)
UnityEngine.GUIUtility.ProcessEvent (System.Int32 instanceID, System.IntPtr nativeEventPtr, System.Boolean& result) (at <89d2c0fe37e0454991daf2807b7dcb63>:0)

Dialogue.cs

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

namespace RPG.Dialogue
{
    [CreateAssetMenu(fileName = "unnamedDialogue", menuName = "Dialogue")]
    public class Dialogue : ScriptableObject
    {
        [SerializeField]
        List<DialogueNode> nodes = new List<DialogueNode>();
        Dictionary<string, DialogueNode> nodeLookup = new Dictionary<string, DialogueNode>();

#if UNITY_EDITOR
        //preprocessor directive -> code will only be included in the editor not in the build
        private void Awake()
        {
            AddDefaultNode();
            PopulateDictionary();
        }

        private void OnValidate()
        {
            PopulateDictionary();
        }
#endif

#if UNITY_STANDALONE && !UNITY_EDITOR
        private void Awake()
        {
            PopulateDictionary();
        }
#endif
        void AddDefaultNode()
        {
            if (nodes.Count == 0)
            {
                DialogueNode rootNode = new DialogueNode();
                rootNode.uniqueID = Guid.NewGuid().ToString();
                nodes.Add(rootNode);
            }

        }

        private void PopulateDictionary()
        {
            nodeLookup.Clear();
            foreach (DialogueNode node in GetAllNodes())
            {
                Debug.Log(node);
                nodeLookup[node.uniqueID] = node;
            }
        }

        public IEnumerable<DialogueNode> GetAllNodes()
        {
            Debug.Log(nodes);
            return nodes;
        }

        public DialogueNode GetRootNode()
        {
            return nodes[0];
        }

        public IEnumerable<DialogueNode> GetAllChildren(DialogueNode parentNode)
        {
            foreach(string childID in parentNode.children)
            {
                if (!nodeLookup.ContainsKey(childID)) { continue; }
                yield return nodeLookup[childID];
            }
        }
    }

}


DialogueNode.cs


using System;
using System.Collections.Generic;
using UnityEngine;

namespace RPG.Dialogue
{
    [Serializable]
    public class DialogueNode
    {
        public string uniqueID;
        public string dialogueName;
        [TextArea]
        public string text;
        public string[] children;
        public Rect rect = new Rect(0,0,200,300);

    }
}
    

DialogueEditor.cs

using System.Drawing.Printing;
using System.Runtime.Remoting.Messaging;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine;

namespace RPG.Dialogue.Editor // can only access stuff up so Editor has acccess to RPG.Dialogue but not the other way around
{
    public class DialogueEditor : EditorWindow
    {
        static Dialogue selectedDialogue = null;
        GUIStyle nodeStyle;
        DialogueNode draggingNode = null;
        Vector2 draggingOffset;

        [MenuItem("Window/Dialogue Editor")]
        public static void ShowEditorWindow()
        {
            GetWindow<DialogueEditor>(false,"Dialogue Editor");
        }

        [OnOpenAsset(1)]
        public static bool OnOpenAsset(int instanceID, int line)
        {
            Dialogue dialogue = EditorUtility.InstanceIDToObject(instanceID) as Dialogue;
            if (dialogue is null) { return true; } // we are handling the Callback and should not go further
            selectedDialogue = dialogue;
            ShowEditorWindow();
            return false;

        }

        private void OnEnable()
        {
            Selection.selectionChanged += OnSelectionChanged;

            nodeStyle = new GUIStyle();
            nodeStyle.normal.background = EditorGUIUtility.Load("node0") as Texture2D;
            nodeStyle.normal.textColor = Color.white;
            nodeStyle.padding = new RectOffset(20, 20, 20, 20);
            nodeStyle.border = new RectOffset(12, 12, 12, 12);
        }

        private void OnSelectionChanged()
        {
            Dialogue newDialogue = Selection.activeObject as Dialogue;
            if (newDialogue is null) { return; }
            selectedDialogue = newDialogue;
            Repaint();
        }

        private void OnGUI()
        {
            if(selectedDialogue == null)
            {
                EditorGUILayout.LabelField("No Dialogue Selected");
                
            }
            else
            {
                ProcessEvents();
                foreach (DialogueNode node in selectedDialogue.GetAllNodes())
                {
                    DrawConnections(node);
                }
                foreach (DialogueNode node in selectedDialogue.GetAllNodes())
                {
                    DrawNodes(node);
                }
            }
            
        }
        void DrawNodes(DialogueNode node)
        {
            GUILayout.BeginArea(node.rect, nodeStyle);
            EditorGUI.BeginChangeCheck();
            EditorGUILayout.LabelField("Name:");
            string newName = EditorGUILayout.TextField(node.dialogueName == "" ? "Add a Name" : node.dialogueName);
            EditorGUILayout.LabelField("Text:");
            string newText = EditorGUILayout.TextArea(node.text == "" ? "Add Text!" : node.text);
            GUILayout.EndArea();

            if (EditorGUI.EndChangeCheck())
            {
                Undo.RecordObject(selectedDialogue, "Update Dialogue Text");
                Undo.RecordObject(selectedDialogue, "Update Dialogue Name");
                node.dialogueName = newName;
                node.text = newText;
            }
        }

        void DrawConnections(DialogueNode node)
        {
            Vector3 startPosition = new Vector2(node.rect.xMax, node.rect.center.y);
            foreach (DialogueNode childNode in selectedDialogue.GetAllChildren(node))
            {
                Vector3 endPosition = new Vector2(childNode.rect.xMin, childNode.rect.center.y);
                Vector3 controlPointOffset = endPosition - startPosition;
                controlPointOffset.y = 0;
                controlPointOffset.x *= 0.8f;
                Handles.DrawBezier(startPosition, endPosition, 
                    startPosition + controlPointOffset, endPosition - controlPointOffset, 
                    Color.white, null, 4f);
            }
        }
            
        private void ProcessEvents()
        {
            if(Event.current.type == EventType.MouseDown && draggingNode == null)
            {
                draggingNode = GetNodeAtPoint(Event.current.mousePosition);
                if(draggingNode != null)
                {
                    draggingOffset = draggingNode.rect.position - Event.current.mousePosition;
                }
            }

            else if(Event.current.type == EventType.MouseDrag && draggingNode != null)
            {
                Undo.RecordObject(selectedDialogue, "Change Node position");
                draggingNode.rect.position = Event.current.mousePosition + draggingOffset;
                GUI.changed = true; // tell GUI to update
            }

            else if(Event.current.type == EventType.MouseUp && draggingNode != null)
            {
                draggingNode = null;
            }
            
        }
        private DialogueNode GetNodeAtPoint(Vector2 point)
        {
            DialogueNode foundNode = null;
            foreach (DialogueNode node in selectedDialogue.GetAllNodes())
            {
                if (node.rect.Contains(point)) 
                {
                    foundNode = node;
                }
            }
            return foundNode;
        }
    }

    

}

Following the error in the code you supplied, it appears the children property of one of the nodes is null. Sam changes the array (string[]) in DialogueNode to a list (List<string>) and initialises it, which you didn’t do

Thank you that seems to fix it. So the problem was that the array was null and there hadn’t been any children added yet but on populateDictonary it tried accessing them?

Yeah, it was null. But the error actually came from the part that’s trying to draw connections between the node and its children

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms