Dialogue Node Disappears when creating new dialogue

Have been following the dialogue-quests course ( core combat and inventory also ) using the latest version of Unity 2022. When creating a new dialogue, the initial dialogue node disappears immediately when clicking anywhere in Unity.

I see others have run into the issue using 2022 and the only work around I have seen is downgrading the version of Unity. This did work for me btw.

Has anyone found a fix for 2022?

reference issue:
https://community.gamedev.tv/t/creating-a-new-dialogue-makes-new-node-disappear-after-submitting/202089

1 Like

I’m still working on a fix for 2022+.

Thank you for looking into this. In the meantime, I have added the following to DialogueEditor->ProcessEvents() as a work around:

            else if(Event.current.button == 2 && !createdInitialNode)
            {
                selectedDialogue.CreateNode(null);
                createdInitialNode = true;
            }

I’m sort of coming to a similar conclusion. Sam’s code exists largely to teach about using OnBeforeSerialize and similar tricks to get the initial node in place. The biggest issue is that until the Dialogue is named, it technically doesn’t exist, at least as far as the filesystem and the Undo system are concerned. We’ve taken steps to work around that, but those steps simply don’t work anymore.

Looking again, I’m not getting the issue of the node vanishing altogether. What I am getting (in both 2022.1 and 2022.2zz) is an error after naming the file that the Variable nodes of Dialogue doesn’t exist anymore, a strange error to say the least. Then yet another new node is created to replace the old node automatically and I’m able to continue as normal.

Now this may be from some other change I made to Dialogue.cs in my fork of the repo, so I’ve included my version of the Dialogue.cs here:

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

namespace RPG.Dialogue
{
    [CreateAssetMenu(fileName = "New Dialogue", menuName = "Dialogue", order = 0)]
    public class Dialogue : ScriptableObject, ISerializationCallbackReceiver
    {
        [SerializeField]
        List<DialogueNode> nodes = new List<DialogueNode>();
        [SerializeField]
        Vector2 newNodeOffset = new Vector2(250, 0);

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

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

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

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

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

        public IEnumerable<DialogueNode> GetPlayerChildren(DialogueNode currentNode)
        {
            foreach (DialogueNode node in GetAllChildren(currentNode))
            {
                if (node.IsPlayerSpeaking())
                {
                    yield return node;
                }
            }
        }


        public IEnumerable<DialogueNode> GetAIChildren(DialogueNode currentNode)
        {
            foreach (DialogueNode node in GetAllChildren(currentNode))
            {
                if (!node.IsPlayerSpeaking())
                {
                    yield return node;
                }
            }
        }



#if UNITY_EDITOR
        public void CreateNode(DialogueNode parent)
        {
            DialogueNode newNode = MakeNode(parent);
            Undo.RegisterCreatedObjectUndo(newNode, "Created Dialogue Node");
            Undo.RecordObject(this, "Added Dialogue Node");
            AddNode(newNode);
        }

        public void DeleteNode(DialogueNode nodeToDelete)
        {
            Undo.RecordObject(this, "Deleted Dialogue Node");
            nodes.Remove(nodeToDelete);
            OnValidate();
            CleanDanglingChildren(nodeToDelete);
            Undo.DestroyObjectImmediate(nodeToDelete);
        }

        private DialogueNode MakeNode(DialogueNode parent)
        {
            DialogueNode newNode = CreateInstance<DialogueNode>();
            newNode.name = Guid.NewGuid().ToString();
            if (parent != null)
            {
                parent.AddChild(newNode.name);
                newNode.SetPlayerSpeaking(!parent.IsPlayerSpeaking());
                newNode.SetPosition(parent.GetRect().position + newNodeOffset);
            }
            
            return newNode;
        }

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

        private void CleanDanglingChildren(DialogueNode nodeToDelete)
        {
            foreach (DialogueNode node in GetAllNodes())
            {
                node.RemoveChild(nodeToDelete.name);
            }
        }

        public Rect CalculateNeededWindowSize()
        {
            Rect rect = new Rect(0,0,Screen.width, Screen.height);
            foreach (var dialogueNode in GetAllNodes())
            {
                rect.xMax = Mathf.Max(rect.xMax, dialogueNode.GetRect().xMax);
                rect.yMax = Mathf.Max(rect.yMax, dialogueNode.GetRect().yMax);
            }
            rect.xMax += newNodeOffset.x;
            rect.yMax += newNodeOffset.x;
            return rect; 
        }
#endif

        public void OnBeforeSerialize()
        {
#if UNITY_EDITOR
            if (nodes.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()
        {
        }
    }
}

The workaround enabled me to move on with the lessons. I believe the overall issue was resolved in lesson 27 or 28. It seems to be working fine now - I create a dialogue and the initial node does not disappear and I can move forward as expected. - keeping my fingers crossed :wink:

Ok, it’s possible that this is why I’m not encountering it, as I’m working off of the final codebase (with my mods along the way).

The fact that I’m getting an error at all, though, is annoying, especially since this little fragment of code:

        private void OnValidate()
        {
            if (nodes == null || nodes.Count == 0) return;
            nodeLookup.Clear();
            foreach (DialogueNode node in nodes)
            {
                nodeLookup[node.name] = node;
            }
        }

should guarantee that no null reference exception can occur… but somehow it’s passing the check on the first line but no longer exists by the foreach line, and that’s impossible.

However, discussing this with Gary in private, we think it’s related to Unity using the Job system to handle many of the tasks in the Editor behind the scenes.

Privacy & Terms