using Unity.Cinemachine; using UnityEngine; using UnityEngine.InputSystem; namespace Jankenbots.Prototype { /// /// 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. /// /// • AvatarFollow — 3rd-person chase on YOUR character (on-foot). /// • RobotFollow — chase cam on the bot (driving; the robot-follow cam). /// • 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 /// (). Press V to cycle manually; claiming /// or leaving a seat re-asserts the sensible default. /// /// 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. /// [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 public float lookHeight = 1.2f; // aim a bit above the target pivot Transform _bot, _standOverlook; CinemachineCamera _avatar, _robot, _stand, _orbit; CinemachineCamera[] _all; Mode _mode = Mode.AvatarFollow; bool _userOverride; bool _lastSeated; float _orbitAngle; PlayerAvatar _local; void Start() { if (GetComponent() == null) gameObject.AddComponent(); // Retire the old bespoke bot-only follow cam if it's still on this camera. var legacy = GetComponent(); if (legacy != null) legacy.enabled = false; var b = GameObject.Find("Bot"); if (b != null) _bot = b.transform; var s = GameObject.Find("StandPoint_0"); if (s != null) _standOverlook = s.transform; var holder = new GameObject("CameraRigs").transform; _avatar = Make(holder, "CM_AvatarFollow"); _robot = Make(holder, "CM_RobotFollow"); _stand = Make(holder, "CM_StandView"); _orbit = Make(holder, "CM_CinematicOrbit"); _all = new[] { _avatar, _robot, _stand, _orbit }; Apply(_mode); } CinemachineCamera Make(Transform parent, string name) { var go = new GameObject(name); go.transform.SetParent(parent, false); var vc = go.AddComponent(); vc.Priority = 0; return vc; } void Update() { if (_local == null) _local = FindLocal(); var kb = Keyboard.current; if (kb != null && kb.vKey.wasPressedThisFrame) { _mode = (Mode)(((int)_mode + 1) % _all.Length); _userOverride = true; Apply(_mode); } // A 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); } } } void LateUpdate() { if (_local != null) PoseFollow(_avatar, _local.transform, avatarOffset); if (_bot != null) { PoseFollow(_robot, _bot, robotOffset); _orbitAngle += orbitSpeed * Time.deltaTime; Vector3 off = Quaternion.Euler(0f, _orbitAngle, 0f) * new Vector3(0f, orbitHeight, -orbitRadius); PoseFollow(_orbit, _bot, off); if (_standOverlook != null) { _stand.transform.position = _standOverlook.position + Vector3.up * 1.5f; _stand.transform.rotation = Quaternion.LookRotation( (_bot.position + Vector3.up * lookHeight) - _stand.transform.position); } } } void PoseFollow(CinemachineCamera vc, Transform target, Vector3 worldOffset) { Vector3 pos = target.position + worldOffset; vc.transform.position = pos; vc.transform.rotation = Quaternion.LookRotation((target.position + Vector3.up * lookHeight) - pos); } PlayerAvatar FindLocal() { foreach (var a in FindObjectsByType(FindObjectsSortMode.None)) if (a.IsOwner) return a; return null; } void Apply(Mode m) { for (int i = 0; i < _all.Length; i++) _all[i].Priority = ((int)m == i) ? 20 : 0; } } }