Game Crashes after I implemented from lecture

I am using Unity 2020.1.17f1, I followed the Udemy Unity RPG Dialogue course, video 43 (Choosing State). When I playtest in the Unity, it crashes as soon as I hit the next button to proceed to the next node. I double check to see if my code is correct and it is.

Thank you.

Eric

What sort of crash? Is it a null reference error, or does the Editor crash and close?

Paste in your Dialogue.cs and PlayerConversant.cs, and we’ll see if we can figure out what’s going on.

I don’t have a null reference, it is when the game is running and I press next to proceed to the next node, the Editor crashes and closes. Sorry I left that out. Here are my scripts.

using System;
using System.Collections;
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 GetAllNodes())
            {
                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 GetAIChildren(currentNode))
            {
                if (node.IsPlayerSpeaking())
                {
                    yield return node;
                }
            }
        }

        public IEnumerable<DialogueNode> GetAIChildren(DialogueNode currentNode)
        {
            foreach (DialogueNode node in GetAIChildren(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);
            }
        }

#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()
        {
            
        }
    }
}
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace RPG.Dialogue
{
    public class PlayerConversant : MonoBehaviour
    {
        [SerializeField] Dialogue currentDialogue;
        DialogueNode currentNode = null;
        bool isChoosing = false;

        private void Awake()
        {
            currentNode = currentDialogue.GetRootNode();
        }

        public bool IsChoosing()
        {
            return isChoosing;
        }

        public string GetText()
        {
            if(currentNode == null)
            {
                return "";
            }
            return currentNode.GetText();
        }

        public IEnumerable<DialogueNode> GetChoices()
        {
            return currentDialogue.GetPlayerChildren(currentNode);
            
        }

        public void Next()
        {
            int numPlayerResponses = currentDialogue.GetPlayerChildren(currentNode).Count();
            if(numPlayerResponses > 0)
            {
                isChoosing = true;
                return;
            }

            DialogueNode[] children = currentDialogue.GetAIChildren(currentNode).ToArray();
            int randomIndex = Random.Range(0, children.Count());  //Random replies in dailogue
            currentNode = children[randomIndex];
        }

        public bool HasNext()
        {
            return currentDialogue.GetAllChildren(currentNode).Count() > 0;
        }
    }
}

This is causing a recursive loop to the depths of hell. GetPlayerChildren is going to cause it, too, because it also calls GetAIChildren which is recursively calling itself. You probably meant to call GetAllChildren(currentNode) in the foreach loop

Sorry Brian. I stole your post

That is what the video showed and I looked at the GitHub to make sure I didn’t make an error.

No. It’s not.

This is what’s in the Github repo:

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


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

This is your code

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

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

It’s easy to miss because it almost looks the same. But it is going to crash Unity. Like it did

2 Likes

Thank you for pointing that out to me, you are correct. Thank you again :+1:

1 Like

It’s an easy thing to miss, and quite common with similar letters (I versus l).

1 Like

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

Privacy & Terms