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.