Has anyone acomplished making the mouse wheel zoom in/out the DialogueEditor to reveal all the connected nodes?
I had a go, and got it some what working, but there is an issue where the visible area of the viewport does not seem to expand with the zoom, so you cant actually see all the nodes they are sort of cropped off (see screen shot)
Attached is my script so far if anyone can easily spot what is going wrong, that would be awesome. Bonus points would be having he background grid image scale up and down also like it does in the animator tool…
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine;
namespace RPG.Dialogue.Editor
{
public static class EditorZoomArea
{
private static Matrix4x4 prevGuiMatrix;
public static void Begin(float zoomScale, Rect screenCoordsArea)
{
prevGuiMatrix = GUI.matrix;
Matrix4x4 translation = Matrix4x4.TRS(screenCoordsArea.center, Quaternion.identity, Vector3.one);
Matrix4x4 scale = Matrix4x4.Scale(new Vector3(zoomScale, zoomScale, 1.0f));
GUI.matrix = translation * scale * translation.inverse * GUI.matrix;
GUI.BeginGroup(new Rect(0, 0, screenCoordsArea.width, screenCoordsArea.height));
}
public static void End()
{
GUI.EndGroup();
GUI.matrix = prevGuiMatrix;
}
}
public class DialogueEditor : EditorWindow
{
Dialogue 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;
float zoom = 1.0f; // Zoom factor
const float zoomMin = 0.5f;
const float zoomMax = 2.0f;
const float canvasSize = 8000;
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)
{
Dialogue dialogue = EditorUtility.InstanceIDToObject(instanceID) as Dialogue;
if(dialogue != null)
{
ShowEditorWindow();
return true;
}
return false;
}
private void OnEnable()
{
Selection.selectionChanged += OnSelectionChange;
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);
playerNodeStyle = new GUIStyle();
playerNodeStyle.normal.background = EditorGUIUtility.Load("node1") as Texture2D;
playerNodeStyle.normal.textColor = Color.white;
playerNodeStyle.padding = new RectOffset(20, 20, 20, 20);
playerNodeStyle.border = new RectOffset(12, 12, 12, 12);
}
private void OnSelectionChange()
{
Dialogue newDialogue = Selection.activeObject as Dialogue;
if(newDialogue != null)
{
selectedDialogue = newDialogue;
Repaint();
}
}
private void OnGUI()
{
if (selectedDialogue == null)
{
EditorGUILayout.LabelField("No Dialogue Selected.");
return;
}
ProcessEvents();
// Dynamically adjust the canvas size based on node positions
Rect canvas = GetCanvasBounds();
// Start Scroll View with zoom adjustment
Rect scaledCanvas = new Rect(canvas.x, canvas.y, canvas.width * zoom, canvas.height * zoom);
scrollPosition = GUI.BeginScrollView(new Rect(0, 0, position.width, position.height), scrollPosition, scaledCanvas);
// Draw Background Texture
Texture2D backgroundTex = UnityEngine.Resources.Load("background") as Texture2D;
Rect texCoords = new Rect(0, 0, canvas.width / backgroundSize, canvas.height / backgroundSize);
GUI.DrawTextureWithTexCoords(canvas, backgroundTex, texCoords);
// Apply Zooming
Matrix4x4 matrixBackup = GUI.matrix;
GUI.matrix = Matrix4x4.TRS(new Vector3(-scrollPosition.x, -scrollPosition.y, 0), Quaternion.identity, new Vector3(zoom, zoom, 1));
foreach (DialogueNode node in selectedDialogue.GetAllNodes())
{
DrawConnections(node);
}
foreach (DialogueNode node in selectedDialogue.GetAllNodes())
{
DrawNode(node);
}
// Restore Matrix
GUI.matrix = matrixBackup;
GUI.EndScrollView();
if (creatingNode != null)
{
selectedDialogue.CreateNode(creatingNode);
creatingNode = null;
}
if (deletingNode != null)
{
selectedDialogue.DeleteNode(deletingNode);
deletingNode = null;
}
}
private Rect GetCanvasBounds()
{
if (selectedDialogue == null || !selectedDialogue.GetAllNodes().Any())
{
return new Rect(0, 0, canvasSize, canvasSize);
}
float minX = float.MaxValue;
float minY = float.MaxValue;
float maxX = float.MinValue;
float maxY = float.MinValue;
foreach (DialogueNode node in selectedDialogue.GetAllNodes())
{
Rect nodeRect = node.GetRect();
if (nodeRect.x < minX) minX = nodeRect.x;
if (nodeRect.y < minY) minY = nodeRect.y;
if (nodeRect.xMax > maxX) maxX = nodeRect.xMax;
if (nodeRect.yMax > maxY) maxY = nodeRect.yMax;
}
float width = (maxX - minX) + 200; // Add padding
float height = (maxY - minY) + 200;
return new Rect(minX, minY, Mathf.Max(width, canvasSize), Mathf.Max(height, canvasSize));
}
private void ProcessEvents()
{
Event e = Event.current;
// Mouse Wheel Zoom
if (e.type == EventType.ScrollWheel)
{
float zoomDelta = -e.delta.y * 0.01f;
float oldZoom = zoom;
zoom = Mathf.Clamp(zoom + zoomDelta, zoomMin, zoomMax);
Repaint();
Vector2 mouseWorldPos = (Event.current.mousePosition + scrollPosition) / oldZoom;
scrollPosition = (mouseWorldPos * zoom) - Event.current.mousePosition;
e.Use();
}
// Drag Nodes or Canvas
if (e.type == EventType.MouseDown && draggingNode == null)
{
draggingNode = GetNodeAtPoint(e.mousePosition / zoom + scrollPosition);
if (draggingNode != null)
{
draggingOffset = draggingNode.GetRect().position - e.mousePosition / zoom;
Selection.activeObject = draggingNode;
}
else
{
draggingCanvas = true;
draggingCanvasOffset = scrollPosition - e.mousePosition / zoom; // Adjust for zoom
Selection.activeObject = selectedDialogue;
}
}
else if (e.type == EventType.MouseDrag)
{
if (draggingNode != null)
{
Undo.RecordObject(selectedDialogue, "Move Dialogue Node");
draggingNode.SetPosition(e.mousePosition / zoom + draggingOffset);
GUI.changed = true;
}
else if (draggingCanvas)
{
// Smooth panning using e.delta
scrollPosition -= e.delta / zoom;
GUI.changed = true;
}
}
else if (e.type == EventType.MouseUp)
{
draggingNode = null;
draggingCanvas = false;
}
else if (e.type == EventType.MouseDrag && draggingCanvas)
{
scrollPosition += e.delta / zoom; // Adjust panning based on zoom
GUI.changed = true;
e.Use();
}
}
private void DrawNode(DialogueNode node)
{
GUIStyle style = nodeStyle;
if (node.IsPlayerSpeaking())
{
style = playerNodeStyle;
}
GUILayout.BeginArea(node.GetRect(), style);
if (selectedDialogue.GetIsDebug())
{
//Node Name Lable
GUIStyle titleStyle = new GUIStyle(EditorStyles.miniLabel);
titleStyle.alignment= TextAnchor.UpperLeft;
titleStyle.fontSize = 8;
GUILayout.Box(node.GetName(),titleStyle, GUILayout.ExpandWidth(false), GUILayout.Height(15));
}
node.SetText(EditorGUILayout.TextField(node.GetText()));
if (GUILayout.Button("Delete"))
{
deletingNode = node;
}
DrawLinkButtons(node);
if (GUILayout.Button("Add Child Node"))
{
creatingNode = node;
}
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"))
{
Undo.RecordObject(selectedDialogue, "Remove Dialogue Link");
linkingParentNode.RemoveChild(node.name);
linkingParentNode = null;
}
}
else
{
if (GUILayout.Button("Child"))
{
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);
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;
}
}
}