My background isn’t tiling, but I checked 3 times if my code is correct, and it seems to be.
DialogueEditor.cs
using System;
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;
[NonSerialized]
GUIStyle nodeStyle;
[NonSerialized]
DialogueNode draggingNode = null;
[NonSerialized]
Vector2 draggingOffset;
[NonSerialized] // because sometimes it still has the old creatingNode in there
DialogueNode creatingNode = null;
[NonSerialized]
DialogueNode deletingNode = null;
[NonSerialized]
DialogueNode linkingParentNode = null;
Vector2 scrollPosition;
Rect editorViewport;
[NonSerialized]
bool isDraggingCanvas = false;
[NonSerialized]
Vector2 draggingCanvasOffset;
[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();
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
Debug.Log("ScrollPosition: " + scrollPosition);
editorViewport = GUILayoutUtility.GetRect(selectedDialogue.editorCanvasSize,selectedDialogue.editorCanvasSize);
Texture2D backgroundTexture = Resources.Load("background") as Texture2D;
Rect backgroundTextureCoordinates = new Rect(0, 0,
selectedDialogue.editorCanvasSize / selectedDialogue.editorBackgroundImageSize,
selectedDialogue.editorCanvasSize / selectedDialogue.editorBackgroundImageSize);
Debug.Log($"Texture Coordinates: {backgroundTextureCoordinates.width} {backgroundTextureCoordinates.height}");
GUI.DrawTextureWithTexCoords(editorViewport, backgroundTexture, backgroundTextureCoordinates);
foreach (DialogueNode node in selectedDialogue.GetAllNodes())
{
DrawConnections(node);
}
foreach (DialogueNode node in selectedDialogue.GetAllNodes())
{
DrawNodes(node);
}
GUILayout.EndScrollView();
if (creatingNode != null)
{
Undo.RecordObject(selectedDialogue, "Added a new Node");
selectedDialogue.CreateNode(creatingNode);
creatingNode = null;
}
if(deletingNode != null)
{
Undo.RecordObject(selectedDialogue, "Deleted a Node");
selectedDialogue.DeleteNode(deletingNode);
deletingNode = null;
}
}
}
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.Height(100));
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(selectedDialogue, "Update Dialogue Text");
Undo.RecordObject(selectedDialogue, "Update Dialogue Name");
node.dialogueName = newName;
node.text = newText;
}
GUILayout.BeginHorizontal();
Color standardColor = GUI.backgroundColor;
GUI.backgroundColor = Color.red;
if (selectedDialogue.GetNodesCount() > 1)
{
if (GUILayout.Button("Remove Node"))
{
deletingNode = node;
}
}
DrawLinkButton(node);
GUI.backgroundColor = Color.green;
if (GUILayout.Button("Add Child"))
{
creatingNode = node;
}
GUI.backgroundColor = standardColor;
GUILayout.EndHorizontal();
GUILayout.EndArea();
}
private void DrawLinkButton(DialogueNode node)
{
if (linkingParentNode == null)
{
GUI.backgroundColor = Color.cyan;
if (GUILayout.Button("Link"))
{
linkingParentNode = node;
}
}
else if (linkingParentNode == node)
{
GUI.backgroundColor = Color.yellow;
if (GUILayout.Button("Cancel"))
{
linkingParentNode = null;
}
}
else if (linkingParentNode.children.Contains(node.uniqueID))
{
Undo.RecordObject(selectedDialogue, "Unlinking Node");
GUI.backgroundColor = Color.yellow;
if (GUILayout.Button("Unlink"))
{
linkingParentNode.children.Remove(node.uniqueID);
}
}
else
{
GUI.backgroundColor = Color.blue;
if (GUILayout.Button("Child"))
{
Undo.RecordObject(selectedDialogue, "Linking Node");
linkingParentNode.children.Add(node.uniqueID);
linkingParentNode = null;
}
}
}
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;
}
if(draggingNode == null)
{
isDraggingCanvas = true;
draggingCanvasOffset = Event.current.mousePosition + scrollPosition;
}
}
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.MouseDrag && isDraggingCanvas)
{
scrollPosition = draggingCanvasOffset - Event.current.mousePosition;
GUI.changed = true; // tell GUI to update
}
else if(Event.current.type == EventType.MouseUp && draggingNode != null)
{
draggingNode = null;
}
else if(Event.current.type == EventType.MouseUp && isDraggingCanvas)
{
isDraggingCanvas = false;
}
}
private DialogueNode GetNodeAtPoint(Vector2 point)
{
DialogueNode foundNode = null;
foreach (DialogueNode node in selectedDialogue.GetAllNodes())
{
if (node.rect.Contains(point+scrollPosition))
{
foundNode = node;
}
}
return foundNode;
}
}
}
Dialogue.cs
using System;
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;
[NonSerialized]
GUIStyle nodeStyle;
[NonSerialized]
DialogueNode draggingNode = null;
[NonSerialized]
Vector2 draggingOffset;
[NonSerialized] // because sometimes it still has the old creatingNode in there
DialogueNode creatingNode = null;
[NonSerialized]
DialogueNode deletingNode = null;
[NonSerialized]
DialogueNode linkingParentNode = null;
Vector2 scrollPosition;
Rect editorViewport;
[NonSerialized]
bool isDraggingCanvas = false;
[NonSerialized]
Vector2 draggingCanvasOffset;
[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();
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
Debug.Log("ScrollPosition: " + scrollPosition);
editorViewport = GUILayoutUtility.GetRect(selectedDialogue.editorCanvasSize,selectedDialogue.editorCanvasSize);
Texture2D backgroundTexture = Resources.Load("background") as Texture2D;
Rect backgroundTextureCoordinates = new Rect(0, 0,
selectedDialogue.editorCanvasSize / selectedDialogue.editorBackgroundImageSize,
selectedDialogue.editorCanvasSize / selectedDialogue.editorBackgroundImageSize);
Debug.Log($"Texture Coordinates: {backgroundTextureCoordinates.width} {backgroundTextureCoordinates.height}");
GUI.DrawTextureWithTexCoords(editorViewport, backgroundTexture, backgroundTextureCoordinates);
foreach (DialogueNode node in selectedDialogue.GetAllNodes())
{
DrawConnections(node);
}
foreach (DialogueNode node in selectedDialogue.GetAllNodes())
{
DrawNodes(node);
}
GUILayout.EndScrollView();
if (creatingNode != null)
{
Undo.RecordObject(selectedDialogue, "Added a new Node");
selectedDialogue.CreateNode(creatingNode);
creatingNode = null;
}
if(deletingNode != null)
{
Undo.RecordObject(selectedDialogue, "Deleted a Node");
selectedDialogue.DeleteNode(deletingNode);
deletingNode = null;
}
}
}
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.Height(100));
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(selectedDialogue, "Update Dialogue Text");
Undo.RecordObject(selectedDialogue, "Update Dialogue Name");
node.dialogueName = newName;
node.text = newText;
}
GUILayout.BeginHorizontal();
Color standardColor = GUI.backgroundColor;
GUI.backgroundColor = Color.red;
if (selectedDialogue.GetNodesCount() > 1)
{
if (GUILayout.Button("Remove Node"))
{
deletingNode = node;
}
}
DrawLinkButton(node);
GUI.backgroundColor = Color.green;
if (GUILayout.Button("Add Child"))
{
creatingNode = node;
}
GUI.backgroundColor = standardColor;
GUILayout.EndHorizontal();
GUILayout.EndArea();
}
private void DrawLinkButton(DialogueNode node)
{
if (linkingParentNode == null)
{
GUI.backgroundColor = Color.cyan;
if (GUILayout.Button("Link"))
{
linkingParentNode = node;
}
}
else if (linkingParentNode == node)
{
GUI.backgroundColor = Color.yellow;
if (GUILayout.Button("Cancel"))
{
linkingParentNode = null;
}
}
else if (linkingParentNode.children.Contains(node.uniqueID))
{
Undo.RecordObject(selectedDialogue, "Unlinking Node");
GUI.backgroundColor = Color.yellow;
if (GUILayout.Button("Unlink"))
{
linkingParentNode.children.Remove(node.uniqueID);
}
}
else
{
GUI.backgroundColor = Color.blue;
if (GUILayout.Button("Child"))
{
Undo.RecordObject(selectedDialogue, "Linking Node");
linkingParentNode.children.Add(node.uniqueID);
linkingParentNode = null;
}
}
}
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;
}
if(draggingNode == null)
{
isDraggingCanvas = true;
draggingCanvasOffset = Event.current.mousePosition + scrollPosition;
}
}
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.MouseDrag && isDraggingCanvas)
{
scrollPosition = draggingCanvasOffset - Event.current.mousePosition;
GUI.changed = true; // tell GUI to update
}
else if(Event.current.type == EventType.MouseUp && draggingNode != null)
{
draggingNode = null;
}
else if(Event.current.type == EventType.MouseUp && isDraggingCanvas)
{
isDraggingCanvas = false;
}
}
private DialogueNode GetNodeAtPoint(Vector2 point)
{
DialogueNode foundNode = null;
foreach (DialogueNode node in selectedDialogue.GetAllNodes())
{
if (node.rect.Contains(point+scrollPosition))
{
foundNode = node;
}
}
return foundNode;
}
}
}
SO Configuration