Why my Bezier Connections are gone?

Hello there, im pretty new here. I bought the rpg package, and I started with the Dialogue and Quest Course; so far I completed 91% of the course, as I did the excersises and used the knowlegde to make the system for my own game. But I encountered a bug, when I made a build and the next day I opened my unity project, the bezier connections that link my dialogues are gone; and it doesn’t matter if i try to connect the nodes manually with the link button, the bezier curves doesn’t draw at all. All I did was a slight modification so I could make a build to test my game (adding a lazy initialization pattern).

Here I will copy-paste my code:

namespace DialogueSystem.Editor {
    public class DialogueEditor : EditorWindow
    {
        DialogScriptable selectedDialogue = null;
        [NonSerialized]
        GUIStyle nodeStyle;
        [NonSerialized]
        GUIStyle playerNodeStyle;
        [NonSerialized]
        DialogueNode draggingNode = null;
        [NonSerialized]
        Vector2 draggingOffset;
        [NonSerialized]
        DialogueNode creatingNode = null;
        [NonSerialized]
        DialogueNode deletingNode = null;

        [NonSerialized]
        DialogueNode linkingParentNode = null;

        Vector2 scrollPosition;

        [NonSerialized]
        bool draggingCanvas = false;
        [NonSerialized]
        Vector2 draggingCanvasOffset;

        const float canvasSize = 4000f;
        const float backgroundSize = 50;


        [MenuItem("Window/Dialogue Editor")]
       public static void ShowEditorWindow()
        {

            GetWindow(typeof(DialogueEditor), false, "Dialogue Editor");
        }

        [OnOpenAsset(1)]
        public static bool OnOpenAsset(int instanceID, int line)
        {
           DialogScriptable dialogue =  EditorUtility.InstanceIDToObject(instanceID) as DialogScriptable;
            if(dialogue != null)
            {
                ShowEditorWindow();
                return true;
            }
            return false;
        }

        private void OnEnable()
        {
            Selection.selectionChanged += OnSelectionChanged;
            nodeStyle = new GUIStyle();
          
            nodeStyle.normal.background = EditorGUIUtility.Load("node0") as Texture2D;
            nodeStyle.padding = new RectOffset(20, 20, 20, 20);
            nodeStyle.border = new RectOffset(12, 12, 12, 12);

            playerNodeStyle = new GUIStyle();

            playerNodeStyle.normal.background = EditorGUIUtility.Load("node1") as Texture2D;
            playerNodeStyle.padding = new RectOffset(20, 20, 20, 20);
            playerNodeStyle.border = new RectOffset(12, 12, 12, 12);
        }

        private void OnSelectionChanged()
        {
            DialogScriptable newDialog = Selection.activeObject as DialogScriptable;

            if(newDialog != null)
            {
                selectedDialogue = newDialog;
                Repaint();
            }
        }

        private void OnGUI()
        {

            if(selectedDialogue == null)
            {
                EditorGUILayout.LabelField("No Dialogue Selected");
            }
            else
            {
                ProcessEvents();
                scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);

                Rect canvas = GUILayoutUtility.GetRect(canvasSize, canvasSize);
               Texture2D backgroundTx = Resources.Load("background") as Texture2D;
                Rect textCoords = new Rect(0, 0, canvasSize/backgroundSize, canvasSize / backgroundSize);
                GUI.DrawTextureWithTexCoords(canvas, backgroundTx, textCoords);



                foreach (DialogueNode nodes in selectedDialogue.GetAllNodes())
                {
                    
                    DrawConnections(nodes);

                }
                foreach (DialogueNode nodes in selectedDialogue.GetAllNodes())
                {
                    DrawNode(nodes);
                    
                   
                }

                EditorGUILayout.EndScrollView();

                if(creatingNode != null)
                {
                    
                    selectedDialogue.CreateNode(creatingNode);
                    creatingNode = null;
                }

                if(deletingNode != null)
                {
                    
                    selectedDialogue.DeleteNode(deletingNode);
                    deletingNode = null;
                }
            }

            
            
        }

        private void ProcessEvents()
        {
            if (Event.current.type == EventType.MouseDown && draggingNode == null)
            {
                draggingNode = GetNodeAtPoint(Event.current.mousePosition + scrollPosition);
                if (draggingNode != null)
                {
                    draggingOffset = draggingNode.GetRect().position - Event.current.mousePosition;
                    Selection.activeObject = draggingNode;
                }
                else
                {
                    draggingCanvas = true;
                    draggingCanvasOffset = Event.current.mousePosition + scrollPosition;
                    Selection.activeObject = selectedDialogue;
                }


            }
            else if (Event.current.type == EventType.MouseDrag && draggingNode != null)
            {
                draggingNode.SetPosition( Event.current.mousePosition + draggingOffset);




                GUI.changed = true;
            }
            else if (Event.current.type == EventType.MouseDrag && draggingCanvas)
            {
                scrollPosition = draggingCanvasOffset - Event.current.mousePosition;

                GUI.changed = true;
            }

            else if (Event.current.type == EventType.MouseUp && draggingNode != null)
            {
                draggingNode = null;

            }
            else if (Event.current.type == EventType.MouseUp && draggingCanvas)
            {

                draggingCanvas = false;
            }
        }
        private DialogueNode GetNodeAtPoint(Vector2 point)
        {
            DialogueNode foundNode = null;
            foreach(DialogueNode node in selectedDialogue.GetAllNodes())
            {
                if (node.GetRect().Contains(point))
                {
                    foundNode = node;

                }
            }
            return foundNode;
        }

        private void DrawNode(DialogueNode node)
        {
            GUIStyle style = nodeStyle;
            if (node.IsPlayerSpeaking())
            {
                style = playerNodeStyle;
            }
            GUILayout.BeginArea(node.GetRect(), style);
            
            node.SetText(EditorGUILayout.TextField(node.GetText()));

           
            GUILayout.BeginHorizontal();
            if (GUILayout.Button("x"))
            {
                deletingNode = node;

            }

            DrawLinkButtons(node);

            if (GUILayout.Button("+"))
            {
                creatingNode = node;

            }
            GUILayout.EndHorizontal();

            GUILayout.EndArea();
        }

        private void DrawLinkButtons(DialogueNode node)
        {
            if (linkingParentNode == null)
            {
                if (GUILayout.Button("link"))
                {
                    linkingParentNode = node;
                }

            }
            else if(linkingParentNode == node)
            {
                if (GUILayout.Button("cancel"))
                {
                   
                    linkingParentNode = null;
                }

            }else if (linkingParentNode.GetChildren().Contains(node.name))
            {
                if (GUILayout.Button("unlink"))
                {
                   
                    linkingParentNode.RemoveChild(node.name);
                    linkingParentNode = null;
                }
            }
            else
            {
                if (GUILayout.Button("child"))
                {
                    
                    linkingParentNode.AddChild(node.name);
                    linkingParentNode = null;
                }
            }
        }

        private void DrawConnections(DialogueNode node)
        {
            Vector3 startPosition = new Vector2(node.GetRect().xMax, node.GetRect().center.y);
            foreach (DialogueNode childNode in selectedDialogue.GetAllChildren(node))
            {
                
                Vector3 endPosition = new Vector2(childNode.GetRect().xMin, childNode.GetRect().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);
            }
        }
    }
}
namespace DialogueSystem
{
    [CreateAssetMenu(fileName = "New Dialogue", menuName = "NPC Dialogues", order = 1)]
    public class DialogScriptable : 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 bool isLazyDictionaryInitialized;

#if UNITY_EDITOR
        private void OnValidate()
        {
            if (nodes.Count == 0)
            {
                CreateNode(null);
            }

            LazyInitializedDictionary();
            
        }
#endif
        private void LazyInitializedDictionary()
        {
            if (isLazyDictionaryInitialized) return;
            nodeLookup.Clear();
            foreach (DialogueNode node in GetAllNodes())
            {
                nodeLookup[node.name] = node;

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

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

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

        public IEnumerable<DialogueNode> GetAllChildren(DialogueNode parentNode)
        {
            LazyInitializedDictionary();
           
            foreach(string childID in parentNode.GetChildren())
            {
               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, "Added Dialogue Node");
            AddNode(newNode);

        }

       

        public void DeleteNode(DialogueNode nodeToDelete)
        {
            Undo.RecordObject(this, "Delete 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);

                if (!parent.IsPlayerSpeaking())
                {
                    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);
            }
        }
#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 rest of the code of the editor is exactly the same as sam’s).

If I open my Unity Project, the bezier curves are fine (before hitting play)

and after I hit play

.

After it the curves dissapear and the HasNext Method tells me the node doesn’t have children.

I suspect the problem is in the LazyInit itself. Looking at our OnValidate, any time OnValidate is called, the Dictionary is rebuilt. This is what we want to happen. In yours, the Dictionary is only built the first time, and never built again. You can test this by simply commenting out the line if(isLazyDictionaryInitialized) return;

Thank you very much! I did it as you suggested, however, if I remove the lazyInit and I make a build, the dialogues in the build are broken (the dictionary tells me there is no child object and furthermore, no next button). How can I fix this?

Fixing the build is actually a separate issue, in that OnValidate is never called from within a built game.

You can add this to your Awake method (make sure both OnValidate and Awake are not in the #IF UNITY_EDITOR block.

I tried to do that, but after i put both methods outside the Unity_Editor block, unity didn’t want to make the build, on the contratry, there was a bunch of supposedly “player compiler errors” but when I checked the scripts everything seemed fine (the messages were ambiguous at best). Then I putted the OnValidate method again in the Unity_Editor block and erased the awake then the build could be done sucessfully.

Thanks for all the help.

A bit later in the course, we’ll be moving the if(nodes.Count==0) to another method… this may be the issue… try this:


        private void OnValidate()
        {
#if UNITY_EDITOR
            if (nodes.Count == 0)
            {
                CreateNode(null);
            }
#endif
            InitializeDictionary();
            
        }

        private void InitializeDictionary()
        {
            
            nodeLookup.Clear();
            foreach (DialogueNode node in GetAllNodes())
            {
                nodeLookup[node.name] = node;

            }
        }

Then in Awake(), add OnValidate()

I stripped the lazy code as it’s in the wrong place anyways. In this particular case, we WANT the dictionary to reinitialize every time we add a connection. The way the Lazy was set up in this case, it would prevent us from keeping track of new nodes and parent child relationships that are edited in after the first LazyInitialize.

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