//$ Copyright 2015-22, Code Respawn Technologies Pvt Ltd - All Rights Reserved $// using System; using UnityEngine; namespace DungeonArchitect.Graphs { /// /// A camera that manages the graph editor's viewport /// [Serializable] public class GraphCamera { float maxAllowedZoom = 6.0f; public float MaxAllowedZoom { get { return maxAllowedZoom; } set { maxAllowedZoom = value; } } [SerializeField] Vector2 position = Vector2.zero; /// /// Position of the camera /// public Vector2 Position { get { return position; } set { position = value; } } public Vector2 ScreenOffset = Vector2.zero; [SerializeField] float zoomLevel = 1; /// /// Zoom scale of the graph camera /// public float ZoomLevel { get { return zoomLevel; } set { zoomLevel = value; } } /// /// Pan the camera along the specified delta value /// /// Delta value to move along the X value /// Delta value to move along the Y value public void Pan(int x, int y) { Pan(new Vector2(x, y)); } /// /// Pan the camera along the specified delta value /// /// The delta offset to move the camera to public void Pan(Vector2 delta) { position += delta * zoomLevel; } /// /// Handles the user mouse and keyboard input /// /// public void HandleInput(Event e) { // Handle zooming if (e.type == EventType.ScrollWheel) { // Grab the original position under the mouse so we can restore it after the zoom var originalGraphPosition = ScreenToWorld(e.mousePosition); float zoomMultiplier = 0.1f; zoomMultiplier *= Mathf.Sign(e.delta.y); zoomLevel = Mathf.Clamp(zoomLevel * (1 + zoomMultiplier), 1, maxAllowedZoom); var newGraphPosition = ScreenToWorld (e.mousePosition); position += originalGraphPosition - newGraphPosition; } // Handle pan int dragButton1 = 1; int dragButton2 = 2; if (e.type == EventType.MouseDrag && (e.button == dragButton1 || e.button == dragButton2)) { if (e.delta.magnitude < 150) { Pan(-e.delta); } } } /// /// Converts world coordinates (in the graph view) into Screen coordinates (relative to the editor window) /// /// The world cooridnates of the graph view /// The screen cooridnates relative to the editor window public Vector2 WorldToScreen(Vector2 worldCoord) { return (worldCoord - position) / zoomLevel + ScreenOffset; } /// /// Converts the Screen coordinates (of the editor window) into the graph's world coordinate /// /// /// The world coordinates in the graph view public Vector2 ScreenToWorld(Vector2 screenCoord) { screenCoord -= ScreenOffset; return screenCoord * zoomLevel + position; } /// /// Converts world coordinates (in the graph view) into Screen coordinates (relative to the editor window) /// /// The world cooridnates of the graph view /// The screen cooridnates relative to the editor window public Rect WorldToScreen(Rect worldCoord) { var screen = worldCoord; screen.position = WorldToScreen(worldCoord.position); screen.size = worldCoord.size / zoomLevel; return screen; } /// /// Converts the Screen coordinates (of the editor window) into the graph's world coordinate /// /// /// The world coordinates in the graph view public Rect ScreenToWorld(Rect screenCoord) { var world = screenCoord; world.position = ScreenToWorld(screenCoord.position); world.size = screenCoord.size * ZoomLevel; return world; } /// /// Moves the camera so most of the nodes are visible /// /// The graph to query /// The bounds of the editor window public void FocusOnBestFit(Graph graph, Rect editorBounds) { if (graph == null) return; if (graph.Nodes.Count > 0) { var graphVisibleSize = editorBounds.size * zoomLevel; Vector2 average = Vector2.zero; foreach (var node in graph.Nodes) { if (node == null) continue; average += node.Bounds.center; } average /= graph.Nodes.Count; position = average - graphVisibleSize / 2.0f; } else { position = Vector3.zero; zoomLevel = 1.0f; } } /// /// Moves the camera to the specified node /// /// The node to focus on /// The bounds of the editor window public void FocusOnNode(GraphNode node, Rect editorBounds) { var graphVisibleSize = editorBounds.size * zoomLevel; var nodePosition = node.Bounds.center; position = nodePosition - graphVisibleSize / 2.0f; } } }