[SOLVED] NullReferenceException Dialogue.OnValidate

In the dialogue system, I keep getting his error when creating a new dialogue scriptable object:

[Worker0] NullReferenceException: Object reference not set to an instance of an object
Dialogue.OnValidate () (at Assets/Scripts/Dialogue/Dialogue.cs:26)
Dialogue.Awake () (at Assets/Scripts/Dialogue/Dialogue.cs:18)

I’m not sure how to fix it :frowning:

Here is my code:

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

    [CreateAssetMenu(fileName = "New Dialogue", menuName = "Dialogue/New Dialogue", order = 0)]
    public class Dialogue : ScriptableObject, ISerializationCallbackReceiver
    {
        [SerializeField] List<DialogueNode> nodesList = new List<DialogueNode>();

        Dictionary<string, DialogueNode> nodeLookUp = new Dictionary<string, DialogueNode>();

        void Awake()
        {
            OnValidate();
        }

        private void OnValidate()
        {
            nodeLookUp.Clear();
            foreach (DialogueNode node in GetAllNodes())
            {
                nodeLookUp[node.name] = node;
            }
        }

        public IEnumerable<DialogueNode> GetAllNodes()
        {
            return nodesList;
        }

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

        public IEnumerable<DialogueNode> GetAllChildren(DialogueNode parentNode)
        {
            foreach (string childID in parentNode.Children)
            {
                if (nodeLookUp.ContainsKey(childID))
                    yield return nodeLookUp[childID];
            }
        }


#if UNITY_EDITOR

        public void CreateNode(DialogueNode parent)
        {
            DialogueNode newNode = MakeNode(parent);
            Undo.RegisterCreatedObjectUndo(newNode, "created dialogue node");
            Undo.RecordObject(this, "new dialogue node");
            AddNode(newNode);
        }      

        private static DialogueNode MakeNode(DialogueNode parent)
        {
            Vector2 rectOffset = new Vector2(300, 0);
            DialogueNode newNode = CreateInstance<DialogueNode>();
            newNode.name = Guid.NewGuid().ToString();

            if (parent != null)
            {
                parent.AddChild(newNode.name);
                newNode.SetPosition(parent.Rect.position + rectOffset);
            }

            return newNode;
        }
        private void AddNode(DialogueNode newNode)
        {
            nodesList.Add(newNode);
            OnValidate();
        }

        public void DeleteNode(DialogueNode nodeToDelete)
        {
            Undo.RecordObject(this, "deleted dialogue node");
            nodesList.Remove(nodeToDelete);
            OnValidate();
            CleanRemainingChildren(nodeToDelete);
            Undo.DestroyObjectImmediate(nodeToDelete);
        }

        private void CleanRemainingChildren(DialogueNode nodeToDelete)
        {
            foreach (DialogueNode node in GetAllNodes())
            {
                node.RemoveChild(nodeToDelete.name);
            }
        }
#endif
        public void OnBeforeSerialize() //When you save a file to the HHD
        {
#if UNITY_EDITOR
            if (nodesList.Count == 0)
            {
                DialogueNode newNode = MakeNode(null);
                AddNode(newNode);
            }

            if (AssetDatabase.GetAssetPath(this) != "")
            {
                foreach (DialogueNode node in GetAllNodes())
                {
                    if (AssetDatabase.GetAssetPath(node) == "")
                    {
                        AssetDatabase.AddObjectToAsset(node, this);
                    }
                }
            }
#endif
        }

        public void OnAfterDeserialize() //When you load a file from the HHD
        {
            //Do nothing
        }
    }

1 Like

Interesting, I would expect the error would be in CreateNode, since when the dialogue is first created, it’s not yet existing in the file system for the Undo setups in that section. (There’s actually a topic about that hiding somewhere in the list of topics tagged dialogue-quests). When I get home from work, I’ll research this further and see if I can trace out why this is null referencing on Awake()/OnValidate()

1 Like

I’m officially stumped… Awake() calls OnValidate()
OnValidate calls nodeLookup.Clear() which wont’ be node because it is initialized in it’s declaration.
Then it iterates over GetAllNodes() which simply returns nodesList, which is also initialized in it’s declaration.
The foreach loop should do nothing if there are no nodes in the nodesList, meaning nodes.name should not create a null reference…

I think I need to take a closer look at the project to see if I can figure out what’s going on. Zip up your project and upload it to https://gdev.tv/projectupload and I’ll take a look at it. Be sure to remove the Library folder to save space.

1 Like

Done! Thank you for your time :smiley:

I have the project, and am downloading 2021.3.0f1, I’ll get a chance to open it proper and have a good look when I get hom tonight from work. I did notice you’re using Odin Inspector. There’s a chance, though it’s slight, that this is the root of the issue. I’ll know later when I start testing the project.

1 Like

So I did manage to get a look at the project before I ran off to work.

I notice that it gives the OnValidate error when creating the Dialogue, but when all is said and done, you have a proper Dialogue with a single node waiting to be edited in the editor. I think what’s happening is that OnValidate() and OnBeforeSerialize are conflicting on that Awake() call…

Try this modification to Awake()

void Awake()
{
     #if UNITY_EDITOR
     #elif
     OnValidate();
     #endif
}

This should prevent the OnValidate call from being called in Awake, giving OnBeforeSerialize a frame to do it’s job (at which point the system will call OnValidate automagicaly). When the game is built into a player, Awake() will call OnValidate as it is supposed to, but at that point the ScriptableObject and it’s fields will be hard baked and ready to go.

1 Like

I tried this, but I’m getting an error from #elif :frowning:

Assets\Scripts\Dialogue\Dialogue.cs(19,12): error CS1517: Invalid preprocessor expression

My bad on that. #else is the correct keyword there. #elif is if you have a different condition to test.

1 Like

Still no luck. The problem seems to actually be in this line of code:

nodeLookUp[node.name] = node;

Commenting it out makes the error not happen again. :thinking:

I just saw there’s another thread with my same problem:

I am too using Unity 2021 (2021.3.0f1).

Maybe it’s due to Unity’s version.

I forgot about that topic. This sort of confirms that there is an issue in Unity 2021, somewhere in the file creation system. In previous versions of Unity, unless you were explicitly using the Jobs system, there was no concept of [Worker0] and [Worker4], etc.

Somehow, we’re getting a race condition behind the scenes (which is difficult to control).

Let’s attack this more directly in OnValidate()

private void OnValidate()
{
     if(nodeLookup!=null) nodeLookUp.Clear();
     foreach (DialogueNode node in GetAllNodes())
     {
           if(node!=null)
           {
                 nodeLookUp[node.name] = node;
            }
      }
4 Likes

Thank you very much! That solved the problem! :partying_face:

It’s still baffling that node could be null in the first place, but I’ve now tested the course project in two different 2021 versions (2.3 and 3.0) and that same error message appears without the filter.

1 Like

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

Privacy & Terms