It has been working correctly previously and i only added the dialogueName in DialogueNode and added a Textfield for that.
Error Message:
NullReferenceException: Object reference not set to an instance of an object
RPG.Dialogue.Dialogue+<GetAllChildren>d__8.MoveNext () (at Assets/Scripts/Dialogue/Dialogue.cs:69)
RPG.Dialogue.Editor.DialogueEditor.DrawConnections (RPG.Dialogue.DialogueNode node) (at Assets/Scripts/Dialogue/Editor/DialogueEditor.cs:95)
RPG.Dialogue.Editor.DialogueEditor.OnGUI () (at Assets/Scripts/Dialogue/Editor/DialogueEditor.cs:64)
UnityEditor.HostView.InvokeOnGUI (UnityEngine.Rect onGUIPosition) (at <1f0be198f5164d2489de92f22c998266>:0)
UnityEditor.DockArea.DrawView (UnityEngine.Rect dockAreaRect) (at <1f0be198f5164d2489de92f22c998266>:0)
UnityEditor.DockArea.OldOnGUI () (at <1f0be198f5164d2489de92f22c998266>:0)
UnityEngine.UIElements.IMGUIContainer.DoOnGUI (UnityEngine.Event evt, UnityEngine.Matrix4x4 parentTransform, UnityEngine.Rect clippingRect, System.Boolean isComputingLayout, UnityEngine.Rect layoutSize, System.Action onGUIHandler, System.Boolean canAffectFocus) (at <f2e1435fec084ff693c649189d4dffd4>:0)
UnityEngine.UIElements.IMGUIContainer.HandleIMGUIEvent (UnityEngine.Event e, UnityEngine.Matrix4x4 worldTransform, UnityEngine.Rect clippingRect, System.Action onGUIHandler, System.Boolean canAffectFocus) (at <f2e1435fec084ff693c649189d4dffd4>:0)
UnityEngine.UIElements.IMGUIContainer.DoIMGUIRepaint () (at <f2e1435fec084ff693c649189d4dffd4>:0)
UnityEngine.UIElements.UIR.RenderChainCommand.ExecuteNonDrawMesh (UnityEngine.UIElements.UIR.DrawParams drawParams, System.Single pixelsPerPoint, System.Exception& immediateException) (at <f2e1435fec084ff693c649189d4dffd4>:0)
Rethrow as ImmediateModeException
UnityEngine.UIElements.UIR.RenderChain.Render () (at <f2e1435fec084ff693c649189d4dffd4>:0)
UnityEngine.UIElements.UIRRepaintUpdater.Update () (at <f2e1435fec084ff693c649189d4dffd4>:0)
UnityEngine.UIElements.VisualTreeUpdater.UpdateVisualTreePhase (UnityEngine.UIElements.VisualTreeUpdatePhase phase) (at <f2e1435fec084ff693c649189d4dffd4>:0)
UnityEngine.UIElements.Panel.UpdateForRepaint () (at <f2e1435fec084ff693c649189d4dffd4>:0)
UnityEngine.UIElements.Panel.Repaint (UnityEngine.Event e) (at <f2e1435fec084ff693c649189d4dffd4>:0)
UnityEngine.UIElements.UIElementsUtility.DoDispatch (UnityEngine.UIElements.BaseVisualElementPanel panel) (at <f2e1435fec084ff693c649189d4dffd4>:0)
UnityEngine.UIElements.UIElementsUtility.UnityEngine.UIElements.IUIElementsUtility.ProcessEvent (System.Int32 instanceID, System.IntPtr nativeEventPtr, System.Boolean& eventHandled) (at <f2e1435fec084ff693c649189d4dffd4>:0)
UnityEngine.UIElements.UIEventRegistration.ProcessEvent (System.Int32 instanceID, System.IntPtr nativeEventPtr) (at <f2e1435fec084ff693c649189d4dffd4>:0)
UnityEngine.UIElements.UIEventRegistration+<>c.<.cctor>b__1_2 (System.Int32 i, System.IntPtr ptr) (at <f2e1435fec084ff693c649189d4dffd4>:0)
UnityEngine.GUIUtility.ProcessEvent (System.Int32 instanceID, System.IntPtr nativeEventPtr, System.Boolean& result) (at <89d2c0fe37e0454991daf2807b7dcb63>:0)
Dialogue.cs
using System;
using System.Collections.Generic;
using UnityEditor.Callbacks;
using UnityEngine;
namespace RPG.Dialogue
{
[CreateAssetMenu(fileName = "unnamedDialogue", menuName = "Dialogue")]
public class Dialogue : ScriptableObject
{
[SerializeField]
List<DialogueNode> nodes = new List<DialogueNode>();
Dictionary<string, DialogueNode> nodeLookup = new Dictionary<string, DialogueNode>();
#if UNITY_EDITOR
//preprocessor directive -> code will only be included in the editor not in the build
private void Awake()
{
AddDefaultNode();
PopulateDictionary();
}
private void OnValidate()
{
PopulateDictionary();
}
#endif
#if UNITY_STANDALONE && !UNITY_EDITOR
private void Awake()
{
PopulateDictionary();
}
#endif
void AddDefaultNode()
{
if (nodes.Count == 0)
{
DialogueNode rootNode = new DialogueNode();
rootNode.uniqueID = Guid.NewGuid().ToString();
nodes.Add(rootNode);
}
}
private void PopulateDictionary()
{
nodeLookup.Clear();
foreach (DialogueNode node in GetAllNodes())
{
Debug.Log(node);
nodeLookup[node.uniqueID] = node;
}
}
public IEnumerable<DialogueNode> GetAllNodes()
{
Debug.Log(nodes);
return nodes;
}
public DialogueNode GetRootNode()
{
return nodes[0];
}
public IEnumerable<DialogueNode> GetAllChildren(DialogueNode parentNode)
{
foreach(string childID in parentNode.children)
{
if (!nodeLookup.ContainsKey(childID)) { continue; }
yield return nodeLookup[childID];
}
}
}
}
DialogueNode.cs
using System;
using System.Collections.Generic;
using UnityEngine;
namespace RPG.Dialogue
{
[Serializable]
public class DialogueNode
{
public string uniqueID;
public string dialogueName;
[TextArea]
public string text;
public string[] children;
public Rect rect = new Rect(0,0,200,300);
}
}
DialogueEditor.cs
using System.Drawing.Printing;
using System.Runtime.Remoting.Messaging;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine;
namespace RPG.Dialogue.Editor // can only access stuff up so Editor has acccess to RPG.Dialogue but not the other way around
{
public class DialogueEditor : EditorWindow
{
static Dialogue selectedDialogue = null;
GUIStyle nodeStyle;
DialogueNode draggingNode = null;
Vector2 draggingOffset;
[MenuItem("Window/Dialogue Editor")]
public static void ShowEditorWindow()
{
GetWindow<DialogueEditor>(false,"Dialogue Editor");
}
[OnOpenAsset(1)]
public static bool OnOpenAsset(int instanceID, int line)
{
Dialogue dialogue = EditorUtility.InstanceIDToObject(instanceID) as Dialogue;
if (dialogue is null) { return true; } // we are handling the Callback and should not go further
selectedDialogue = dialogue;
ShowEditorWindow();
return false;
}
private void OnEnable()
{
Selection.selectionChanged += OnSelectionChanged;
nodeStyle = new GUIStyle();
nodeStyle.normal.background = EditorGUIUtility.Load("node0") as Texture2D;
nodeStyle.normal.textColor = Color.white;
nodeStyle.padding = new RectOffset(20, 20, 20, 20);
nodeStyle.border = new RectOffset(12, 12, 12, 12);
}
private void OnSelectionChanged()
{
Dialogue newDialogue = Selection.activeObject as Dialogue;
if (newDialogue is null) { return; }
selectedDialogue = newDialogue;
Repaint();
}
private void OnGUI()
{
if(selectedDialogue == null)
{
EditorGUILayout.LabelField("No Dialogue Selected");
}
else
{
ProcessEvents();
foreach (DialogueNode node in selectedDialogue.GetAllNodes())
{
DrawConnections(node);
}
foreach (DialogueNode node in selectedDialogue.GetAllNodes())
{
DrawNodes(node);
}
}
}
void DrawNodes(DialogueNode node)
{
GUILayout.BeginArea(node.rect, nodeStyle);
EditorGUI.BeginChangeCheck();
EditorGUILayout.LabelField("Name:");
string newName = EditorGUILayout.TextField(node.dialogueName == "" ? "Add a Name" : node.dialogueName);
EditorGUILayout.LabelField("Text:");
string newText = EditorGUILayout.TextArea(node.text == "" ? "Add Text!" : node.text);
GUILayout.EndArea();
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(selectedDialogue, "Update Dialogue Text");
Undo.RecordObject(selectedDialogue, "Update Dialogue Name");
node.dialogueName = newName;
node.text = newText;
}
}
void DrawConnections(DialogueNode node)
{
Vector3 startPosition = new Vector2(node.rect.xMax, node.rect.center.y);
foreach (DialogueNode childNode in selectedDialogue.GetAllChildren(node))
{
Vector3 endPosition = new Vector2(childNode.rect.xMin, childNode.rect.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);
}
}
private void ProcessEvents()
{
if(Event.current.type == EventType.MouseDown && draggingNode == null)
{
draggingNode = GetNodeAtPoint(Event.current.mousePosition);
if(draggingNode != null)
{
draggingOffset = draggingNode.rect.position - Event.current.mousePosition;
}
}
else if(Event.current.type == EventType.MouseDrag && draggingNode != null)
{
Undo.RecordObject(selectedDialogue, "Change Node position");
draggingNode.rect.position = Event.current.mousePosition + draggingOffset;
GUI.changed = true; // tell GUI to update
}
else if(Event.current.type == EventType.MouseUp && draggingNode != null)
{
draggingNode = null;
}
}
private DialogueNode GetNodeAtPoint(Vector2 point)
{
DialogueNode foundNode = null;
foreach (DialogueNode node in selectedDialogue.GetAllNodes())
{
if (node.rect.Contains(point))
{
foundNode = node;
}
}
return foundNode;
}
}
}