Keep the pan limit constant across zoom and hand off cleanly to free cam

Two paint-mode pan refinements:
- The max-distance clamp is back to body-relative only (CurrentScale, no zoom
  factor), so the world-space pan limit is identical at every zoom level;
  only the sensitivity stays zoom-scaled (finer when zoomed in).
- Don't zero the pan offset in OnDisable. The only thing that disables this
  component is FreeCamMode taking over, and the freecam snaps to the live
  (still panned) view. Zeroing here jumped the orbit camera the brain blends
  FROM to center while the freecam sat at the panned pose - the visible
  "snap to 0, then translate to the offset" on the paint->free-cam switch.
  Leaving the offset frozen keeps blend-from == blend-to (both panned) for a
  seamless handoff; the pan glides home on its own once the orbit input next
  runs in a non-paint mode.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 4.8 committed 2026-06-30 19:53:35 +02:00
1 parent 5965f7199a
commit 6fcada4631
1 file changed
+19 -15
+19 -15
View File
@@ -79,8 +79,8 @@ public class ThirdPersonOrbitInput : MonoBehaviour
"zoom (closer in = finer). Body scale is folded in automatically via the orbit radius.")]
public float panSensitivity = 0.004f;
[Tooltip("Farthest the framing can pan from center, measured at the default zoom. It scales with the zoom " +
"distance too, so the maximum on-screen pan reach stays the same at every zoom level.")]
[Tooltip("Farthest the framing can pan from center, in world units (body scale folded in). This is NOT scaled " +
"by zoom, so the maximum pan distance stays the same at every zoom level - only the sensitivity changes.")]
public float panMaxDistance = 1f;
[Tooltip("How snappily the pan glides back to center when paint mode is left (1/second; 0 = snaps home " +
@@ -142,9 +142,12 @@ public class ThirdPersonOrbitInput : MonoBehaviour
void OnDisable()
{
SetCursorLocked(false);
// Drop any live pan so a disabled rig doesn't leave a stale framing offset behind.
_pan = Vector2.zero;
if (_camOffset) _camOffset.Offset = Vector3.zero;
// Deliberately DON'T zero the pan here. The only thing that disables this component is FreeCamMode
// taking over, and the freecam snaps to the live (still panned) view. Resetting the offset now would
// jump the orbit camera the brain blends FROM to center while the freecam sits at the panned pose -
// the visible "snap to 0, then translate to the offset" on the paint->freecam switch. Leaving the
// offset frozen keeps blend-from == blend-to (both panned) -> a seamless handoff. The pan then glides
// home on its own once the orbit input runs again in a non-paint mode (PanEnabled false).
}
/// <summary>Uniform scale of the current follow target (1 when none, or when Scale With Target is off).
@@ -157,13 +160,13 @@ public class ThirdPersonOrbitInput : MonoBehaviour
return t ? Mathf.Max(0.0001f, t.lossyScale.x) : 1f;
}
/// <summary>How much the pan scales with the live zoom distance. Dividing the orbit radius by the default
/// radius makes this exactly 1 at the default zoom (so the authored panSensitivity/panMaxDistance keep their
/// feel there), smaller when zoomed in (finer pan, less reach) and larger when zoomed out. The body scale
/// cancels out because the radius already embeds it - a bigger player orbits proportionally farther - so the
/// pan stays body-relative across player sizes at every zoom, just like CurrentScale() did at the default
/// radius. The result: the model slides the same amount ON SCREEN per pixel, and the same maximum reach
/// across the screen, regardless of how far the wheel has zoomed.</summary>
/// <summary>How much the pan SENSITIVITY scales with the live zoom distance. Dividing the orbit radius by the
/// default radius makes this exactly 1 at the default zoom (so the authored panSensitivity keeps its feel
/// there), smaller when zoomed in (finer pan) and larger when zoomed out. The body scale cancels out because
/// the radius already embeds it - a bigger player orbits proportionally farther - so the pan stays
/// body-relative across player sizes at every zoom. The result: the model slides the same amount ON SCREEN
/// per pixel regardless of how far the wheel has zoomed. The clamp does NOT use this - panMaxDistance is
/// body-relative only (CurrentScale), so the world-space pan limit is constant across zoom levels.</summary>
float PanZoomScale() => _orbit ? _orbit.Radius / Mathf.Max(0.0001f, defaultRadius) : 1f;
/// <summary>Snaps the zoom to the scaled default radius. Called when a new avatar binds (see
@@ -349,10 +352,11 @@ public class ThirdPersonOrbitInput : MonoBehaviour
{
Vector2 d = ReadMouseDelta() * MouseSensitivity.Multiplier;
// Subtracting the drag gives a hand-tool feel (the model follows the cursor: drag right -> model
// slides right). Both the step and the clamp scale with the orbit distance so the pan feels the same
// on screen at every zoom - finer when zoomed in, and the same maximum reach across the screen.
// slides right). The step scales with the orbit distance so it feels the same on screen at every
// zoom (finer when zoomed in). The clamp stays body-relative only (no zoom factor), so the maximum
// pan distance in world units is the same at every zoom level.
_pan -= d * (panSensitivity * zoom);
_pan = Vector2.ClampMagnitude(_pan, panMaxDistance * zoom);
_pan = Vector2.ClampMagnitude(_pan, panMaxDistance * CurrentScale());
}
else if (active)
{