首页 > 解决方案 > 如何围绕点缩放 Matrix4x4?

问题描述

我正在 Unity 中编写一个自定义编辑器窗口,我希望能够在其中滚动/滚动并拖动视图。为此,我一直设置GUI.matrixMatrix4x4.TRS(offset, Quaternion.identity, Vector3.one * scale),我可以控制offsetscale。这很好用,除非在滚动进/出时,它会锚定窗口的左上角。我希望它锚定在鼠标的位置上

如果这只需要在缩放时更改偏移量,那就太好了 - 我只是不确定偏移量应该是多少。Matrix4x4s超出了我的数学舒适区。

这是我目前处理缩放的方式:

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?*/;
}

标签: c#unity3dmathunity-editorunity-ui

解决方案


让我们尝试了解 GUI 矩阵的作用。它表示一个转换,它在世界空间(您的 GUI 对象所在的位置)中获取坐标并将它们转换为 GUI 空间(或多或少与您的窗口对齐)。由于我们没有旋转,我们可以很容易地解释构造矩阵TRS()对世界空间点的作用pWorld

pGUI = scale * pWorld + offset

现在您要更改scalescaleNew. 这样做时,您希望在鼠标下保持相同的世界位置。

如果你的鼠标位置是在 GUI 空间中给出的(例如 from Event.current.mousePosition),那么我们首先需要找到对应的世界空间点:

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

我们想在鼠标下修复这个点,即:

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

我们可以解决这个问题以获得新的偏移量:

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

就是这样。

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.


推荐阅读