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