diff --git a/Assets/Scripts/GameModeManager.cs b/Assets/Scripts/GameModeManager.cs index 9cf7597..21f3113 100644 --- a/Assets/Scripts/GameModeManager.cs +++ b/Assets/Scripts/GameModeManager.cs @@ -194,7 +194,15 @@ public class GameModeManager : MonoBehaviour if (cameraInput) { var vcam = cameraInput.GetComponent(); - if (vcam) { vcam.Follow = camTarget; vcam.LookAt = camTarget; } + if (vcam) + { + vcam.Follow = camTarget; + vcam.LookAt = camTarget; + // Follow is now the new avatar's target, so its lossyScale is live - start the camera at this + // avatar's scaled default zoom (Hider pulls in, Hunter pushes out) instead of inheriting the + // previous avatar's radius. + cameraInput.ResetZoom(); + } } if (firstPerson) diff --git a/Assets/Scripts/ThirdPersonOrbitInput.cs b/Assets/Scripts/ThirdPersonOrbitInput.cs index 514f5ea..b05cdd3 100644 --- a/Assets/Scripts/ThirdPersonOrbitInput.cs +++ b/Assets/Scripts/ThirdPersonOrbitInput.cs @@ -45,18 +45,29 @@ public class ThirdPersonOrbitInput : MonoBehaviour [Tooltip("Mouse wheel zooms the camera in and out (changes orbit radius).")] public bool enableZoom = true; - [Tooltip("World units the orbit radius changes per mouse wheel notch.")] + [Tooltip("Scale the zoom range, step and default radius by the follow target's size (PlayerScaler), " + + "so a small (Hider) or large (Hunter) player is framed at the same body-relative distance. " + + "The radius fields below are then size-1 design values, multiplied by the target's scale at " + + "runtime. Off = absolute world units regardless of player size.")] + public bool scaleWithTarget = true; + + [Tooltip("World units the orbit radius changes per mouse wheel notch (size-1 design value; scaled by " + + "the target when Scale With Target is on).")] public float zoomSpeed = 0.5f; [Tooltip("Eases the orbit radius toward the zoom target (higher = snappier, 0 = instant).")] public float zoomSmoothing = 10f; - [Tooltip("Closest orbit radius (zoom in).")] + [Tooltip("Closest orbit radius / zoom in (size-1 design value; scaled by the target).")] public float minRadius = 1.5f; - [Tooltip("Farthest orbit radius (zoom out).")] + [Tooltip("Farthest orbit radius / zoom out (size-1 design value; scaled by the target).")] public float maxRadius = 8f; + [Tooltip("Orbit radius applied when the camera (re)binds to a player - the starting zoom level " + + "(size-1 design value; scaled by the target). Set so the bound player is comfortably framed.")] + public float defaultRadius = 4.5f; + [Header("Cursor")] [Tooltip("Lock and hide the cursor while the camera is actively rotating.")] public bool manageCursor = true; @@ -91,6 +102,27 @@ public class ThirdPersonOrbitInput : MonoBehaviour void OnDisable() => SetCursorLocked(false); + /// Uniform scale of the current follow target (1 when none, or when Scale With Target is off). + /// The zoom range, step and default radius are multiplied by this so the framing is body-relative across + /// player sizes. PlayerScaler keeps the player uniformly scaled, so a single lossyScale component is exact. + float CurrentScale() + { + if (!scaleWithTarget || !_orbit) return 1f; + Transform t = _orbit.FollowTarget; + return t ? Mathf.Max(0.0001f, t.lossyScale.x) : 1f; + } + + /// Snaps the zoom to the scaled default radius. Called when a new avatar binds (see + /// GameModeManager.BindLocalPlayer) so each player starts at its body-relative default zoom instead of + /// inheriting the previous avatar's radius. + public void ResetZoom() + { + if (!_orbit) return; + _targetRadius = defaultRadius * CurrentScale(); + _orbit.Radius = _targetRadius; + _zoomInit = true; + } + void Update() { if (!_orbit) @@ -129,20 +161,30 @@ public class ThirdPersonOrbitInput : MonoBehaviour { if (!_zoomInit) { - _targetRadius = _orbit.Radius; + // Seed the starting zoom at the scaled default (not the live radius) so a fresh bind frames + // the player body-relative; ResetZoom() does the same on an explicit (re)bind. + _targetRadius = defaultRadius * CurrentScale(); + _orbit.Radius = _targetRadius; _zoomInit = true; } + float scale = CurrentScale(); + // Shift+wheel is reserved for the paint brush resize (MeshPainter), so the orbit camera // ignores the wheel while Shift is held - otherwise it would zoom and resize at once. float scroll = ShiftHeld() ? 0f : ReadScroll(); if (!Mathf.Approximately(scroll, 0f)) { - // One wheel notch = one zoom step (sign only), independent of the - // platform's raw wheel magnitude. Scrolling up zooms in. - _targetRadius = Mathf.Clamp(_targetRadius - Mathf.Sign(scroll) * zoomSpeed, minRadius, maxRadius); + // One wheel notch = one zoom step (sign only), independent of the platform's raw wheel + // magnitude. Scrolling up zooms in. Step scales with the player so the number of notches to + // cross the range is the same at every size. + _targetRadius -= Mathf.Sign(scroll) * zoomSpeed * scale; } + // Clamp every frame against the scaled range so a runtime size change (PlayerScaler grow/shrink) + // keeps the target in-range instead of sticking at a stale absolute bound. + _targetRadius = Mathf.Clamp(_targetRadius, minRadius * scale, maxRadius * scale); + // Ease the actual radius toward the target (frame-rate independent). _orbit.Radius = zoomSmoothing > 0f ? Mathf.Lerp(_orbit.Radius, _targetRadius, 1f - Mathf.Exp(-zoomSmoothing * Time.deltaTime))