Hi, I have a problem, can I ask you please? When I created new dialogue by scriptable objects and click on the dialog (or deselect dialogue in the project window) in dialogue editor the dialogue in the editor disappears. Where is the problem? Unity version? I have this one: 2022.3.5. I think, I have the code same like in the tutorial. I’ve tried to follow this topic: bunch-of-errors-after-deleting-the-4-dialogues, but there were some errors
Here are the 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
{
[SerializeField]
List<DialogueNode> nodes = new List<DialogueNode>();
Dictionary<string, DialogueNode> nodeLookup = new Dictionary<string, DialogueNode>();
// make sure awake is called in the editor, not in the game built etc.
#if UNITY_EDITOR
private void Awake()
{
if (nodes.Count == 0)
{
CreateNode(null);
}
}
#endif
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.children)
{
if (nodeLookup.ContainsKey(childID))
{
yield return nodeLookup[childID];
}
}
}
public void CreateNode(DialogueNode parent)
{
DialogueNode newNode = CreateInstance<DialogueNode>();
newNode.name = Guid.NewGuid().ToString();
Undo.RegisterCreatedObjectUndo(newNode, "Created Dialogue Node");
if (parent != null)
{
parent.children.Add(newNode.name);
}
nodes.Add(newNode);
OnValidate();
}
public void DeleteNode(DialogueNode nodeToDelete)
{
nodes.Remove(nodeToDelete);
Undo.DestroyObjectImmediate(nodeToDelete);
OnValidate();
CleanDanglingChildren(nodeToDelete);
}
private void CleanDanglingChildren(DialogueNode nodeToDelete)
{
foreach (DialogueNode node in GetAllNodes())
{
node.children.Remove(nodeToDelete.name);
}
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace RPG.Dialogue
{
public class DialogueNode : ScriptableObject
{
public string text;
public List<string> children = new List<string>();
public Rect rect = new Rect(0, 0, 200, 100);
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.Callbacks;
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 = 400;
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
{
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)
{
Undo.RecordObject(selectedDialogue, "Added Dialogue Node");
selectedDialogue.CreateNode(creatingNode);
creatingNode = null;
}
if (deletingNode != null)
{
Undo.RecordObject(selectedDialogue, "Deleted Dialogue Node");
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.rect.position - Event.current.mousePosition;
//------------ 13/12
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)
{
Undo.RecordObject(selectedDialogue, "Move Dialogue Node");
draggingNode.rect.position = 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.rect, nodeStyle);
EditorGUI.BeginChangeCheck();
string newText = EditorGUILayout.TextField(node.text);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(selectedDialogue, "Update Dialogue Text");
node.text = newText;
}
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.children.Contains(node.name))
{
if (GUILayout.Button("Unlink"))
{
Undo.RecordObject(selectedDialogue, "Remove Dialogue Link");
linkingParentNode.children.Remove(node.name);
linkingParentNode = null;
}
}
else
{
if (GUILayout.Button("child"))
{
Undo.RecordObject(selectedDialogue, "Add Dialogue Link");
linkingParentNode.children.Add(node.name);
linkingParentNode = null;
}
}
}
private 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);
// 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.rect.Contains(point))
{
foundNode = node;
}
}
return foundNode;
}
}
}