1

I'm writing a custom editor window in Unity in which I would like to be able to both scroll in/out and drag the view around. To do so, I've been setting GUI.matrix to Matrix4x4.TRS(offset, Quaternion.identity, Vector3.one * scale), where I have control over offset and scale. This works fine, except when scrolling in/out, it anchors the top left of the window. I would like it to anchor on the mouse's position.

If this just requires changing the offset when zooming, that's great - I'm just not sure what the offset should be here. Matrix4x4s are out of my comfort zone for math.

Here is how I'm currently handling zooming:

if (Event.current.type == EventType.ScrollWheel)
{
    _scale *= Math.Sign(Event.current.delta.y) == 1 ? 1.1f : 1f / 1.1f;
    _offset += Math.Sign(Event.current.delta.y) * /*What do I put here?*/;
}
  • The closest I've gotten is by multiplying the cursor's position (using `Matrix.MultiplyPoint(v3)`) both before and after changing the scale and afterwards subtract `after - before` from the offset. Here's a [gif](https://i.imgur.com/U6m8UsP.gif) showing the results. Notice how the contents go slightly above the cursor rather than getting indefinitely closer to it. – CatSandwich Jun 24 '21 at 03:09
  • When you tried this, did you also change `_offset` as shown in the question or did you leave that part away? Also, is `v3` in world space? – Nico Schertler Jun 25 '21 at 14:55
  • @NicoSchertler To clarify what I tried, I did change the offset. I ran the mouse's position through the transformation matrix both before and after changing the scale. I then subtracted the difference between the resultants from the _offset. That's what gave me the closest option as shown in the gif. v3 in this case was the mouse's position (relative to the window). – CatSandwich Jun 25 '21 at 19:38
  • I think a lot of the trouble I'm running into stems from confusion about which values I have are relative to the screen, relative to the rect (window), which incorporate _scale, and which incorporate _offset. There's so much going on that it's a bit overwhelming. – CatSandwich Jun 25 '21 at 19:41

1 Answers1

1

Let's try to understand whatthe GUI matrix does. It represents a transform that takes coordinates in world space (where your GUI objects live) and converts them to GUI space (more or less aligned with your window). Since we have no rotation, we can easily interpret what constructing the matrix with TRS() does to a world-space point pWorld:

pGUI = scale * pWorld + offset

Now you want to change scale to scaleNew. In doing so, you want to keep the same world position under the mouse.

If your mouse position is given in GUI space (e.g., from Event.current.mousePosition), then we first need to find the corresponding world space point:

v3World = (1.0 / scaleOld) * (v3GUI - offsetOld)

And we want to fix this point under the mouse, i.e.:

v3GUI = scaleNew * v3World + offsetNew
v3GUI = scaleNew / scaleOld * (v3GUI - offsetOld) + offsetNew 

We can solve this to get the new offset:

v3GUI = scaleNew / scaleOld * v3GUI - scaleNew / scaleOld * offsetOld + offsetNew
(1 - scaleNew / scaleOld) * v3GUI + scaleNew / scaleOld * offsetOld = offsetNew

And that's it.

Btw, you can also do this with matrix operations alone. This is what GUIUtility.ScaleAroundPivot() does. This is how it looks:

newMatrix = T(v3GUI) * S(newScale / oldScale) * T(-v3GUI) * oldMatrix

T represents a translation and S a scaling. The translation pair T(v3GUI) and T(-v3GUI) move the temporary origin of the coordinate system to your mouse position and perform the scaling from there. You could then directly read offset and scale from this matrix.

Nico Schertler
  • 32,049
  • 4
  • 39
  • 70
  • Gonna mark as accepted even though I can't test at the moment. I went ahead and wrote ScreenToWorld and WorldToScreen utility functions. From there I did the approach in another comment of calculating the screen point before and after changing the zoom. By removing the difference, the screen point effectively stayed still when zooming. Here is the relevant code: https://hatebin.com/ivvyenykpq – CatSandwich Jun 26 '21 at 22:52