-
-
Save MattRix/7b4da243dacd43025bc6eae2ee3c3a1a to your computer and use it in GitHub Desktop.
using UnityEngine; | |
// Helper Rect extension methods | |
public static class RectExtensions | |
{ | |
public static Vector2 TopLeft(this Rect rect) | |
{ | |
return new Vector2(rect.xMin, rect.yMin); | |
} | |
public static Rect ScaleSizeBy(this Rect rect, float scale) | |
{ | |
return rect.ScaleSizeBy(scale, rect.center); | |
} | |
public static Rect ScaleSizeBy(this Rect rect, float scale, Vector2 pivotPoint) | |
{ | |
Rect result = rect; | |
result.x -= pivotPoint.x; | |
result.y -= pivotPoint.y; | |
result.xMin *= scale; | |
result.xMax *= scale; | |
result.yMin *= scale; | |
result.yMax *= scale; | |
result.x += pivotPoint.x; | |
result.y += pivotPoint.y; | |
return result; | |
} | |
public static Rect ScaleSizeBy(this Rect rect, Vector2 scale) | |
{ | |
return rect.ScaleSizeBy(scale, rect.center); | |
} | |
public static Rect ScaleSizeBy(this Rect rect, Vector2 scale, Vector2 pivotPoint) | |
{ | |
Rect result = rect; | |
result.x -= pivotPoint.x; | |
result.y -= pivotPoint.y; | |
result.xMin *= scale.x; | |
result.xMax *= scale.x; | |
result.yMin *= scale.y; | |
result.yMax *= scale.y; | |
result.x += pivotPoint.x; | |
result.y += pivotPoint.y; | |
return result; | |
} | |
} | |
public class EditorZoomArea | |
{ | |
private const float kEditorWindowTabHeight = 21.0f; | |
private static Matrix4x4 _prevGuiMatrix; | |
public static Rect Begin(float zoomScale, Rect screenCoordsArea) | |
{ | |
GUI.EndGroup(); // End the group Unity begins automatically for an EditorWindow to clip out the window tab. This allows us to draw outside of the size of the EditorWindow. | |
Rect clippedArea = screenCoordsArea.ScaleSizeBy(1.0f / zoomScale, screenCoordsArea.TopLeft()); | |
clippedArea.y += kEditorWindowTabHeight; | |
GUI.BeginGroup(clippedArea); | |
_prevGuiMatrix = GUI.matrix; | |
Matrix4x4 translation = Matrix4x4.TRS(clippedArea.TopLeft(), Quaternion.identity, Vector3.one); | |
Matrix4x4 scale = Matrix4x4.Scale(new Vector3(zoomScale, zoomScale, 1.0f)); | |
GUI.matrix = translation * scale * translation.inverse * GUI.matrix; | |
return clippedArea; | |
} | |
public static void End() | |
{ | |
GUI.matrix = _prevGuiMatrix; | |
GUI.EndGroup(); | |
GUI.BeginGroup(new Rect(0.0f, kEditorWindowTabHeight, Screen.width, Screen.height)); | |
} | |
} |
using System; | |
using System.Collections.Generic; | |
using UnityEngine; | |
using UnityEditor; | |
public class ZoomTestWindow : EditorWindow | |
{ | |
[MenuItem("Window/Zoom Test")] | |
private static void Init() | |
{ | |
ZoomTestWindow window = EditorWindow.GetWindow<ZoomTestWindow>(false, "Zoom Test"); | |
window.minSize = new Vector2(600.0f, 300.0f); | |
window.wantsMouseMove = true; | |
window.Show(); | |
EditorWindow.FocusWindowIfItsOpen<ZoomTestWindow>(); | |
} | |
private const float kZoomMin = 0.1f; | |
private const float kZoomMax = 10.0f; | |
private readonly Rect _zoomArea = new Rect(0.0f, 75.0f, 600.0f, 300.0f - 100.0f); | |
private float _zoom = 1.0f; | |
private Vector2 _zoomCoordsOrigin = Vector2.zero; | |
private Vector2 ConvertScreenCoordsToZoomCoords(Vector2 screenCoords) | |
{ | |
return (screenCoords - _zoomArea.TopLeft()) / _zoom + _zoomCoordsOrigin; | |
} | |
private void DrawZoomArea() | |
{ | |
// Within the zoom area all coordinates are relative to the top left corner of the zoom area | |
// with the width and height being scaled versions of the original/unzoomed area's width and height. | |
EditorZoomArea.Begin(_zoom, _zoomArea); | |
GUI.Box(new Rect(0.0f - _zoomCoordsOrigin.x, 0.0f - _zoomCoordsOrigin.y, 100.0f, 25.0f), "Zoomed Box"); | |
// You can also use GUILayout inside the zoomed area. | |
GUILayout.BeginArea(new Rect(300.0f - _zoomCoordsOrigin.x, 70.0f - _zoomCoordsOrigin.y, 130.0f, 50.0f)); | |
GUILayout.Button("Zoomed Button 1"); | |
GUILayout.Button("Zoomed Button 2"); | |
GUILayout.EndArea(); | |
EditorZoomArea.End(); | |
} | |
private void DrawNonZoomArea() | |
{ | |
GUI.Box(new Rect(0.0f, 0.0f, 600.0f, 50.0f), "Adjust zoom of middle box with slider or mouse wheel.\nMove zoom area dragging with middle mouse button or Alt+left mouse button."); | |
//zoom into the center | |
Vector2 center = _zoomCoordsOrigin + (_zoomArea.size/2f) / _zoom; | |
float oldZoom = _zoom; | |
_zoom = EditorGUI.Slider(new Rect(0.0f, 50.0f, 600.0f, 25.0f), _zoom, kZoomMin, kZoomMax); | |
_zoom = Mathf.Clamp(_zoom, kZoomMin, kZoomMax); | |
_zoomCoordsOrigin += (center - _zoomCoordsOrigin) - (oldZoom / _zoom) * (center - _zoomCoordsOrigin); | |
GUI.Box(new Rect(0.0f, 300.0f - 25.0f, 600.0f, 25.0f), "Unzoomed Box"); | |
} | |
private void HandleEvents() | |
{ | |
// Allow adjusting the zoom with the mouse wheel as well. In this case, use the mouse coordinates | |
// as the zoom center instead of the top left corner of the zoom area. This is achieved by | |
// maintaining an origin that is used as offset when drawing any GUI elements in the zoom area. | |
if (Event.current.type == EventType.ScrollWheel) | |
{ | |
Vector2 screenCoordsMousePos = Event.current.mousePosition; | |
Vector2 delta = Event.current.delta; | |
Vector2 zoomCoordsMousePos = ConvertScreenCoordsToZoomCoords(screenCoordsMousePos); | |
float zoomDelta = -delta.y / 150.0f; | |
float oldZoom = _zoom; | |
_zoom += zoomDelta; | |
_zoom = Mathf.Clamp(_zoom, kZoomMin, kZoomMax); | |
_zoomCoordsOrigin += (zoomCoordsMousePos - _zoomCoordsOrigin) - (oldZoom / _zoom) * (zoomCoordsMousePos - _zoomCoordsOrigin); | |
Event.current.Use(); | |
} | |
// Allow moving the zoom area's origin by dragging with the middle mouse button or dragging | |
// with the left mouse button with Alt pressed. | |
if (Event.current.type == EventType.MouseDrag && ((Event.current.button == 0 && Event.current.modifiers == EventModifiers.Alt) || Event.current.button == 2)) | |
{ | |
Vector2 delta = Event.current.delta; | |
delta /= _zoom; | |
_zoomCoordsOrigin -= delta; | |
Event.current.Use(); | |
} | |
} | |
public void OnGUI() | |
{ | |
HandleEvents(); | |
// The zoom area clipping is sometimes not fully confined to the passed in rectangle. At certain | |
// zoom levels you will get a line of pixels rendered outside of the passed in area because of | |
// floating point imprecision in the scaling. Therefore, it is recommended to draw the zoom | |
// area first and then draw everything else so that there is no undesired overlap. | |
DrawZoomArea(); | |
DrawNonZoomArea(); | |
} | |
} |
I also just updated the code in DrawNonZoomArea
so that it zooms into the center when you drag the slider.
Can I change from 150 in zoomDelta = -delta.y / 150.0f;
to something else? I tried to change it to some lower value (e.g 50) to make scrolling faster but it wouldn't scroll precisely to the mouse position. Also the more I zoom in the slower it feels. How can I make it feel more consistent (faster)? (as I said above, changing the number to something lower will make it less accurate)
Hmm, I can't see any reason why decreasing it would make it less accurate. I believe the reason it feels slower as you get zoomed in is because it's changing linearly. I think if you make the change multiplicative, it will feel more consistent. Ex. right now zoom does something similar to going through 1, 2, 3, 4, 5 - instead it could be 1, 2, 4, 8, 16
Sorry, I just checked it again and realized that I was calling the ConvertScreenCoordsToZoomCoords after changing the zoom so it wouldn't work properly. I fix it now and I can finally decrease the value. I think changing the value multiplicatively is the solution. Can you show me how to do it? In your other gist, I saw you doing some multiplying but I didn't understand it properly.
This is mostly the same as the code in the post, but I fixed some errors/warnings that were showing up in Unity 2019. I also fixed the if statement on line 76, since it was missing parentheses around the second condition.