Hi can I ask you please? I have a problem, when I creating new dialogues and nodes, I can se only New dialogue and one child in project window. Is it correct? I see every child node in the video.
Also I have this error:
[Worker0] Broken text PPtr in file(Assets/Scripts/ScriptableObjects/Dialogue/New Dialogue.asset). Local file identifier (-5528215956715742925) doesn’t exist!
Not sure if this two problems are related, because in another topic was mentioned it is not a problem if everything works.
Unity version: 2022.3.5f1 (mac)
Here is the printscreen:
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>();
Dictionary<string, DialogueNode> nodeLookup = new Dictionary<string, DialogueNode>();
private void OnValidate()
{
nodeLookup.Clear();
foreach (DialogueNode node in GetAllNodes())
{
if (node != null)
{
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];
}
}
}
#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 static DialogueNode MakeNode(DialogueNode parent)
{
DialogueNode newNode = CreateInstance<DialogueNode>();
newNode.name = Guid.NewGuid().ToString();
if (parent != null)
{
parent.AddChild(newNode.name);
}
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 UnityEngine;
using UnityEditor;
namespace RPG.Dialogue
{
public class DialogueNode : ScriptableObject
{
[SerializeField] string text;
[SerializeField] List<string> children = new List<string>();
[SerializeField] Rect rect = new Rect(0, 0, 200, 100);
public Rect GetRect()
{
return rect;
}
public string GetText()
{
return text;
}
public List<string> GetChildren()
{
return children;
}
#if UNITY_EDITOR
public void SetPosition(Vector2 newPosition)
{
Undo.RecordObject(this, "Move Dialogue Node");
rect.position = newPosition;
EditorUtility.SetDirty(this);
}
public void SetText(string newText)
{
if (newText != text)
{
Undo.RecordObject(this, "Update Dialogue Text");
text = newText;
EditorUtility.SetDirty(this);
}
}
public void AddChild(string childID)
{
Undo.RecordObject(this, "Add Dialogue Link");
children.Add(childID);
EditorUtility.SetDirty(this);
}
public void RemoveChild(string childID)
{
Undo.RecordObject(this, "Remove Dialogue Link");
children.Remove(childID);
EditorUtility.SetDirty(this);
}
#endif
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.Callbacks;
using System.Linq;
namespace RPG.Dialogue.Editor
{
public class DialogueEditor : EditorWindow
{
Dialogue selectedDialogue = null;
[NonSerialized] GUIStyle nodeStyle;
[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 = 4000;
const float backgroundSize = 50;
[MenuItem("Window/Dialogue Editor")]
public static void ShowEditorWindow()
{
GetWindow(typeof(DialogueEditor), false, "Dialogue Editor");
}
// Callback that gets called when an asset is double-clicked in the Unity editor. In this case, if the asset is a Dialogue object, it opens the dialogue editor window.
[OnOpenAssetAttribute(1)]
public static bool OnOpenAsset(int instanceID, int line)
{
Dialogue dialogue = EditorUtility.InstanceIDToObject(instanceID) as Dialogue;
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);
}
private void OnSelectionChanged()
{
Dialogue newDialogue = Selection.activeObject as Dialogue;
if (newDialogue != null)
{
selectedDialogue = newDialogue;
Repaint(); // if user selects dialogue it is updated also in the Dialogue window
}
}
void OnGUI()
{
if (selectedDialogue == null)
{
EditorGUILayout.LabelField("No dialogue selected");
}
else
{
//Following if is important, created dialoge wan't appear:
if (selectedDialogue.GetAllNodes().FirstOrDefault() == null)
{
selectedDialogue.CreateNode(null);
}
ProcessEvents();
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
Rect canvas = GUILayoutUtility.GetRect(canvasSize, canvasSize);
Texture2D backgroundTex = Resources.Load("background") as Texture2D;
Rect texCoords = new Rect(0, 0, canvasSize / backgroundSize, canvasSize / backgroundSize);
GUI.DrawTextureWithTexCoords(canvas, backgroundTex, texCoords);
// if two following foreachs are only one, bezier curve is on top of the node rectangle
foreach (DialogueNode node in selectedDialogue.GetAllNodes())
{
DrawConnections(node);
}
foreach (DialogueNode node in selectedDialogue.GetAllNodes())
{
DrawNode(node);
}
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;
}
// Record drag offset and dragging
else
{
draggingCanvas = true;
draggingCanvasOffset = Event.current.mousePosition + draggingOffset;
Selection.activeObject = selectedDialogue;
}
}
else if (Event.current.type == EventType.MouseDrag && draggingNode != null)
{
draggingNode.SetPosition(Event.current.mousePosition + draggingOffset);
GUI.changed = true; // or: Repaint();
}
//Update scrollPosition
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 void DrawNode(DialogueNode node)
{
GUILayout.BeginArea(node.GetRect(), nodeStyle);
EditorGUI.BeginChangeCheck();
node.SetText(EditorGUILayout.TextField(node.GetText()));
GUILayout.BeginHorizontal();
if (GUILayout.Button(new GUIContent("+", "Button"), new GUIStyle(GUI.skin.button) { normal = new GUIStyleState() { textColor = Color.green } }))
{
creatingNode = node;
}
DrawLinkButtons(node);
if (GUILayout.Button("-"))
{
deletingNode = 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"))
{
//??????????? delete next line?
Undo.RecordObject(selectedDialogue, "Add Dialogue Link");
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);
// Nice drawing of bezier curves:
Vector3 controlPointOffset = endPosition - startPosition;
controlPointOffset.y = 0;
controlPointOffset.x *= 0.8f;
Handles.DrawBezier(
startPosition, endPosition,
startPosition + controlPointOffset,
endPosition - controlPointOffset,
Color.white, null, 4f);
}
}
private DialogueNode GetNodeAtPoint(Vector2 point)
{
DialogueNode foundNode = null;
foreach (DialogueNode node in selectedDialogue.GetAllNodes())
{
if (node.GetRect().Contains(point))
{
foundNode = node;
}
}
return foundNode;
}
}
}