Of course, im passing now my Dialogue.cs:
using System;
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEditor;
using UnityEngine;
namespace RPG.Dialogue{
[CreateAssetMenu(fileName = "New Dialogue", menuName = "RPG/Dialogue")]
public class Dialogue : ScriptableObject, ISerializationCallbackReceiver{
public Vector2 size = new Vector2(7000, 1000);
Dictionary<string, DialogueNode> nodeLookUp = new Dictionary<string, DialogueNode>();
[NonSerialized] List<DialogueNode> nodes = new List<DialogueNode>();
[NonSerialized] Vector2 newNodeOffset = new Vector2(500, 0);
// PUBLIC
public IEnumerable<DialogueNode> GetAllNodes(){
return nodes;
}
public DialogueNode GetRootNode(){
return nodes[0];
}
public IEnumerable<DialogueNode> GetAllChildren(DialogueNode parentNode){
foreach (string child in parentNode.GetChildren())
if (nodeLookUp.ContainsKey(child)) yield return nodeLookUp[child];
}
public void OnBeforeSerialize(){
#if UNITY_EDITOR
if (nodes.Count == 0){
DialogueNode child = MakeNode(null);
AddNode(child);
}
if (AssetDatabase.GetAssetPath(this) == "") return;
foreach(DialogueNode node in GetAllNodes()){
if(AssetDatabase.GetAssetPath(node) == "") AssetDatabase.AddObjectToAsset(node, this);
}
#endif
}
public void OnAfterDeserialize(){
}
// UNITY EDITOR
#if UNITY_EDITOR
public void CreateNode(DialogueNode parent){
DialogueNode newChild = MakeNode(parent);
Undo.RegisterCreatedObjectUndo(newChild, "New node created");
Undo.RecordObject(this, "New Dialogue Node");
AddNode(newChild);
}
public void DeleteNode(DialogueNode toDelete){
Undo.RecordObject(this, "Delete Dialogue Node");
nodes.Remove(toDelete);
CleanChildren(toDelete);
Undo.DestroyObjectImmediate(toDelete);
OnValidate();
}
private DialogueNode MakeNode(DialogueNode parent){
DialogueNode newChild = CreateInstance<DialogueNode>();
newChild.name = Guid.NewGuid().ToString();
if (parent != null){
parent.AddChild(newChild.name);
newChild.SetPlayerSpeaking(!parent.IsPlayerSpeaking());
newChild.SetPosition(parent.GetRect().position + newNodeOffset);
}
return newChild;
}
private void AddNode(DialogueNode newChild){
nodes.Add(newChild);
OnValidate();
}
private void CleanChildren(DialogueNode toDelete){
foreach(DialogueNode node in GetAllNodes()) node.RemoveChild(toDelete.name);
}
#endif
// PRIVATE
private void Awake()
{
OnValidate();
}
private void OnValidate(){
nodeLookUp.Clear();
foreach (DialogueNode node in GetAllNodes()) nodeLookUp[node.name] = node;
}
}
}
And here it’s my DialogueEditor.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.Callbacks;
using System;
namespace RPG.Dialogue.Editor{
public class DialogueEditor : EditorWindow{
// CONFIG
Dialogue selectedDialogue = null;
Vector2 scrollPosition;
[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;
[NonSerialized] bool draggingScrolling = false;
[NonSerialized] Vector2 draggingScrollingOffset;
const float backgroundSize = 50;
// Equivalent of start method UnityEngine
[MenuItem("Window/Dialogue Editor")]
public static void ShowEditorWindow(Dialogue candidate = null){
DialogueEditor window = GetWindow(typeof(DialogueEditor), false, "Dialogue Editor") as DialogueEditor;
if (candidate) window.OnSelectionChange();
}
[OnOpenAsset(1)]
public static bool OnOpenAsset(int instanceID, int line){
var candidate = EditorUtility.InstanceIDToObject(instanceID) as Dialogue;
if (candidate != null){
ShowEditorWindow(candidate);
return true;
}
return false;
}
private void OnEnable(){
Selection.selectionChanged += OnSelectionChange;
nodeStyle = new GUIStyle();
nodeStyle.normal.background = EditorGUIUtility.Load("node0") as Texture2D;
//To change color use nodeStyle.normal.textColor = Color.*desire color*
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 OnSelectionChange(){
Dialogue activeDialogue = Selection.activeObject as Dialogue;
if (activeDialogue != null){
selectedDialogue = activeDialogue;
Repaint();
}
}
private void OnGUI(){
if(selectedDialogue == null) EditorGUILayout.LabelField("No Dialogue Selected");
else{
ProcessEvents();
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
Rect canvas = GUILayoutUtility.GetRect(selectedDialogue.size.x, selectedDialogue.size.y);
Texture2D backgroundTexture = Resources.Load("background") as Texture2D;
Rect textureSize = new Rect(0, 0,
selectedDialogue.size.x / backgroundSize, selectedDialogue.size.y / backgroundSize);
GUI.DrawTextureWithTexCoords(canvas,backgroundTexture, textureSize);
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;
}else{
draggingScrolling = true;
draggingScrollingOffset = 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 && draggingScrolling){
scrollPosition = draggingScrollingOffset - Event.current.mousePosition;
GUI.changed = true;
}else if (Event.current.type == EventType.MouseUp && draggingNode != null){
draggingNode = null;
}else if(Event.current.type == EventType.MouseUp && draggingScrolling){
draggingScrolling = false;
}
}
private void DrawNode(DialogueNode node){
GUIStyle style = nodeStyle;
if (node.IsPlayerSpeaking()) style = playerNodeStyle;
GUILayout.BeginArea(node.GetRect(), style);
//If you add EditorStyle.*choose style*, you can change the color of the label
//EditorGUILayout.LabelField("Node:");
node.SetText(EditorGUILayout.TextField(node.GetText()));
GUILayout.BeginHorizontal();
if (GUILayout.Button("Create dialogue")) creatingNode = node;
DrawLinkButtons(node);
if (GUILayout.Button("Delete")) 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")){
linkingParentNode.AddChild(node.name);
linkingParentNode = null;
}
}
}
private void DrawConnections(DialogueNode node){
Vector2 start = new Vector2(node.GetRect().xMax, node.GetRect().center.y);
foreach (DialogueNode child in selectedDialogue.GetAllChildren(node)){
Vector2 end = new Vector2(child.GetRect().xMin, child.GetRect().center.y);
Vector2 controlOffPoint = end - start;
controlOffPoint.y = 0;
controlOffPoint.x *= 0.8f;
Handles.DrawBezier(start, end, start + controlOffPoint, end - controlOffPoint,
Color.white, null, 4f);
}
}
private DialogueNode GetNodeAtPoint(Vector2 point){
DialogueNode toReturn = null;
foreach(DialogueNode node in selectedDialogue.GetAllNodes())
if(node.GetRect().Contains(point)) toReturn = node;
return toReturn;
}
}
}