camera: RMB-drag look + scroll zoom on follow cams, big zoom-out range
Avatar/Robot follow cams now orbit from player input: hold right-mouse + drag to look around (yaw/pitch clamped), scroll to zoom (3..80m so you can pull back and survey the arena). Per-mode default distance on switch, look angle persists. Stand/Cinematic unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e92be317f5
commit
3082644dbd
1 changed files with 77 additions and 33 deletions
|
|
@ -5,50 +5,68 @@ using UnityEngine.InputSystem;
|
|||
namespace Jankenbots.Prototype
|
||||
{
|
||||
/// <summary>
|
||||
/// JANKENBOTS — client-local camera director. Owns four Cinemachine cameras and
|
||||
/// lets the local player cycle between them; the CinemachineBrain on this Camera
|
||||
/// blends smoothly on every switch.
|
||||
/// JANKENBOTS — client-local camera director. Owns four Cinemachine cameras, lets
|
||||
/// the local player cycle between them and orbit/zoom the follow cams; the
|
||||
/// CinemachineBrain on this Camera blends smoothly on every switch.
|
||||
///
|
||||
/// • AvatarFollow — 3rd-person chase on YOUR character (on-foot).
|
||||
/// • RobotFollow — chase cam on the bot (driving; the robot-follow cam).
|
||||
/// • AvatarFollow — orbitable 3rd-person cam on YOUR character (on-foot).
|
||||
/// • RobotFollow — orbitable chase cam on the bot (driving).
|
||||
/// • StandView — fixed view from your player stand, looking at the bot.
|
||||
/// • CinematicOrbit— slow auto-orbit of the bot (spectate / flavor).
|
||||
///
|
||||
/// The default mode auto-follows the control state: on foot → AvatarFollow, seated
|
||||
/// (driving) → RobotFollow — reading the SAME seat flag as the control handoff
|
||||
/// (<see cref="PlayerAvatar.IsSeatedLocal"/>). Press V to cycle manually; claiming
|
||||
/// or leaving a seat re-asserts the sensible default.
|
||||
/// LOOK / ZOOM (Avatar & Robot only): hold RIGHT MOUSE and drag to orbit
|
||||
/// (yaw/pitch), scroll wheel to zoom in/out (out to <see cref="maxDistance"/> so
|
||||
/// you can pull back and see the arena). RMB-drag keeps the cursor free for the
|
||||
/// Claim/Leave buttons until the walk-up interaction exists.
|
||||
///
|
||||
/// The vcams are intentionally "bare" (no procedural body/aim) — the director poses
|
||||
/// their transforms each LateUpdate from the live targets, and the Brain just blends
|
||||
/// between whichever is prioritised. Cameras are purely client-local, never networked.
|
||||
/// Default mode auto-follows the control state (on foot → Avatar, seated → Robot),
|
||||
/// reading the same seat flag as the control handoff (<see cref="PlayerAvatar.IsSeatedLocal"/>).
|
||||
/// Press V to cycle manually. Cameras are purely client-local, never networked.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Camera))]
|
||||
public class CameraDirector : MonoBehaviour
|
||||
{
|
||||
public enum Mode { AvatarFollow, RobotFollow, StandView, CinematicOrbit }
|
||||
|
||||
[Header("Framing")]
|
||||
public Vector3 avatarOffset = new Vector3(0f, 4f, -6f);
|
||||
public Vector3 robotOffset = new Vector3(0f, 8f, -12f);
|
||||
public float orbitRadius = 20f;
|
||||
public float orbitHeight = 9f;
|
||||
public float orbitSpeed = 18f; // deg/sec
|
||||
[Header("Follow distances")]
|
||||
public float avatarDistance = 8f;
|
||||
public float robotDistance = 16f;
|
||||
|
||||
[Header("Look / zoom (Avatar & Robot)")]
|
||||
[Tooltip("Degrees of orbit per pixel of mouse drag (while holding RMB).")]
|
||||
public float lookSensitivity = 0.18f;
|
||||
[Tooltip("Metres of zoom per scroll notch.")]
|
||||
public float zoomSensitivity = 4f;
|
||||
public float minDistance = 3f;
|
||||
[Tooltip("How far you can pull the camera back. Big so you can survey the arena.")]
|
||||
public float maxDistance = 80f;
|
||||
public float minPitch = -8f;
|
||||
public float maxPitch = 78f;
|
||||
public float lookHeight = 1.2f; // aim a bit above the target pivot
|
||||
|
||||
[Header("Cinematic orbit")]
|
||||
public float orbitRadius = 22f;
|
||||
public float orbitHeight = 10f;
|
||||
public float orbitSpeed = 16f; // deg/sec
|
||||
|
||||
Transform _bot, _standOverlook;
|
||||
CinemachineCamera _avatar, _robot, _stand, _orbit;
|
||||
CinemachineCamera[] _all;
|
||||
Mode _mode = Mode.AvatarFollow;
|
||||
bool _userOverride;
|
||||
bool _lastSeated;
|
||||
float _orbitAngle;
|
||||
|
||||
// Orbit state shared by the two follow cams (player-controlled).
|
||||
float _yaw;
|
||||
float _pitch = 18f;
|
||||
float _dist = 8f;
|
||||
float _cineAngle;
|
||||
|
||||
PlayerAvatar _local;
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (GetComponent<CinemachineBrain>() == null) gameObject.AddComponent<CinemachineBrain>();
|
||||
// Retire the old bespoke bot-only follow cam if it's still on this camera.
|
||||
var legacy = GetComponent<FollowCamera>();
|
||||
if (legacy != null) legacy.enabled = false;
|
||||
|
||||
|
|
@ -85,31 +103,50 @@ namespace Jankenbots.Prototype
|
|||
Apply(_mode);
|
||||
}
|
||||
|
||||
// A seat claim/leave re-asserts the sensible default (clears a manual override).
|
||||
// Seat claim/leave re-asserts the sensible default (clears a manual override).
|
||||
if (_local != null && _local.IsSeatedLocal != _lastSeated)
|
||||
{
|
||||
_lastSeated = _local.IsSeatedLocal;
|
||||
_userOverride = false;
|
||||
}
|
||||
|
||||
if (!_userOverride && _local != null)
|
||||
{
|
||||
Mode want = _local.IsSeatedLocal ? Mode.RobotFollow : Mode.AvatarFollow;
|
||||
if (want != _mode) { _mode = want; Apply(_mode); }
|
||||
}
|
||||
|
||||
// Look / zoom input — only for the orbitable follow cams.
|
||||
if (_mode == Mode.AvatarFollow || _mode == Mode.RobotFollow)
|
||||
{
|
||||
var m = Mouse.current;
|
||||
if (m != null)
|
||||
{
|
||||
if (m.rightButton.isPressed)
|
||||
{
|
||||
Vector2 d = m.delta.ReadValue();
|
||||
_yaw += d.x * lookSensitivity;
|
||||
_pitch = Mathf.Clamp(_pitch - d.y * lookSensitivity, minPitch, maxPitch);
|
||||
}
|
||||
float scroll = m.scroll.ReadValue().y;
|
||||
if (Mathf.Abs(scroll) > 0.01f)
|
||||
_dist = Mathf.Clamp(_dist - Mathf.Sign(scroll) * zoomSensitivity, minDistance, maxDistance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
if (_local != null) PoseFollow(_avatar, _local.transform, avatarOffset);
|
||||
if (_local != null) PoseOrbit(_avatar, _local.transform);
|
||||
|
||||
if (_bot != null)
|
||||
{
|
||||
PoseFollow(_robot, _bot, robotOffset);
|
||||
PoseOrbit(_robot, _bot);
|
||||
|
||||
_orbitAngle += orbitSpeed * Time.deltaTime;
|
||||
Vector3 off = Quaternion.Euler(0f, _orbitAngle, 0f) * new Vector3(0f, orbitHeight, -orbitRadius);
|
||||
PoseFollow(_orbit, _bot, off);
|
||||
_cineAngle += orbitSpeed * Time.deltaTime;
|
||||
Vector3 focus = _bot.position + Vector3.up * lookHeight;
|
||||
Vector3 cineOff = Quaternion.Euler(0f, _cineAngle, 0f) * new Vector3(0f, orbitHeight, -orbitRadius);
|
||||
_orbit.transform.position = focus + cineOff;
|
||||
_orbit.transform.rotation = Quaternion.LookRotation(focus - _orbit.transform.position);
|
||||
|
||||
if (_standOverlook != null)
|
||||
{
|
||||
|
|
@ -120,11 +157,14 @@ namespace Jankenbots.Prototype
|
|||
}
|
||||
}
|
||||
|
||||
void PoseFollow(CinemachineCamera vc, Transform target, Vector3 worldOffset)
|
||||
// Player-controllable orbit: spherical offset from the target driven by _yaw/_pitch/_dist.
|
||||
void PoseOrbit(CinemachineCamera vc, Transform target)
|
||||
{
|
||||
Vector3 pos = target.position + worldOffset;
|
||||
Vector3 focus = target.position + Vector3.up * lookHeight;
|
||||
Vector3 dir = Quaternion.Euler(_pitch, _yaw, 0f) * Vector3.back;
|
||||
Vector3 pos = focus + dir * _dist;
|
||||
vc.transform.position = pos;
|
||||
vc.transform.rotation = Quaternion.LookRotation((target.position + Vector3.up * lookHeight) - pos);
|
||||
vc.transform.rotation = Quaternion.LookRotation(focus - pos);
|
||||
}
|
||||
|
||||
PlayerAvatar FindLocal()
|
||||
|
|
@ -138,6 +178,10 @@ namespace Jankenbots.Prototype
|
|||
{
|
||||
for (int i = 0; i < _all.Length; i++)
|
||||
_all[i].Priority = ((int)m == i) ? 20 : 0;
|
||||
|
||||
// Reset zoom to the mode's default distance on switch (keep the look angle).
|
||||
if (m == Mode.AvatarFollow) _dist = avatarDistance;
|
||||
else if (m == Mode.RobotFollow) _dist = robotDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue