using UnityEngine;
#if ENABLE_INPUT_SYSTEM
using UnityEngine.InputSystem;
#endif
///
/// Fixed lobby camera. The view doesn't move on its own or with the mouse: left clicking a
/// ViewTarget (the tank, the computer monitor) glides the camera face on and close to it, and
/// right clicking glides back to the lobby view the scene was built with.
///
[RequireComponent(typeof(Camera))]
public class LobbyCamera : MonoBehaviour
{
[Tooltip("Seconds for the glide between views.")]
public float moveTime = 0.8f;
[Tooltip("The lobby view, set by the scene builder. Used on start and for Back, whatever pose the camera was saved in.")]
public bool hasHomePose;
public Vector3 homePosition;
public Quaternion homeRotation = Quaternion.identity;
Camera cam;
Vector3 homePos, fromPos, toPos;
Quaternion homeRot, fromRot, toRot;
float t = 1f;
ViewTarget current, hover;
/// Label of the close-up the camera is in, or null in the lobby view.
public static string CurrentLabel { get; private set; }
/// When true (a browser has the keyboard), only F1 leaves the close-up; Esc/Backspace/scroll are passed through.
public static bool CaptureKeys;
static LobbyCamera instance;
public static void RequestView(ViewTarget t) { if (instance != null) instance.GoTo(t); }
ILobbyClickable hoverClick;
void Start()
{
cam = GetComponent();
instance = this;
if (hasHomePose) transform.SetPositionAndRotation(homePosition, homeRotation);
homePos = toPos = transform.position;
homeRot = toRot = transform.rotation;
}
void ReadMouse(out Vector2 pos, out bool left, out bool right)
{
// "Back" is a right click, Esc, Backspace, or scrolling the wheel toward you
#if ENABLE_INPUT_SYSTEM
var m = Mouse.current; var k = Keyboard.current;
pos = m != null ? m.position.ReadValue() : Vector2.zero;
left = m != null && m.leftButton.wasPressedThisFrame;
right = (m != null && (m.rightButton.wasPressedThisFrame || m.scroll.ReadValue().y < -0.1f))
|| (k != null && (k.escapeKey.wasPressedThisFrame || k.backspaceKey.wasPressedThisFrame));
#else
pos = Input.mousePosition;
left = Input.GetMouseButtonDown(0);
right = Input.GetMouseButtonDown(1) || Input.mouseScrollDelta.y < -0.1f || Input.GetKeyDown(KeyCode.Escape) || Input.GetKeyDown(KeyCode.Backspace);
#endif
}
void Update()
{
ReadMouse(out var mouse, out bool left, out bool right);
hover = null; hoverClick = null;
if (current == null && cam != null)
{
var ray = cam.ScreenPointToRay(mouse);
var hits = Physics.RaycastAll(ray, cam.farClipPlane);
System.Array.Sort(hits, (a, b) => a.distance.CompareTo(b.distance));
foreach (var h in hits)
{
var click = h.collider.GetComponent();
if (click != null) { hoverClick = click; break; }
var vt = h.collider.GetComponentInParent();
if (vt != null) { hover = vt; break; }
}
}
if (left && hoverClick != null) hoverClick.Click();
else if (left && hover != null) GoTo(hover);
else if (right && current != null && !CaptureKeys) GoHome();
if (t < 1f)
{
t = Mathf.Min(1f, t + Time.deltaTime / Mathf.Max(0.01f, moveTime));
float e = t * t * (3f - 2f * t); // ease in and out
transform.SetPositionAndRotation(Vector3.Lerp(fromPos, toPos, e), Quaternion.Slerp(fromRot, toRot, e));
}
}
void GoTo(ViewTarget target)
{
var b = target.Bounds;
var rot = Quaternion.Euler(target.pitch, 0f, 0f);
float halfV = cam.fieldOfView * 0.5f * Mathf.Deg2Rad;
float halfH = Mathf.Atan(Mathf.Tan(halfV) * cam.aspect);
float dist = Mathf.Max(b.extents.y / Mathf.Tan(halfV), b.extents.x / Mathf.Tan(halfH)) * target.padding + b.extents.z;
Begin(b.center - rot * Vector3.forward * dist, rot);
current = target;
CurrentLabel = target.label;
}
void GoHome()
{
Begin(homePos, homeRot);
current = null;
CurrentLabel = null;
}
void Begin(Vector3 pos, Quaternion rot)
{
fromPos = transform.position; fromRot = transform.rotation;
toPos = pos; toRot = rot; t = 0f;
}
void OnGUI()
{
var style = new GUIStyle(GUI.skin.label) { fontSize = 15 };
style.normal.textColor = new Color(1f, 1f, 1f, 0.85f);
if (current != null && !CaptureKeys && GUI.Button(new Rect(14, 40, 90, 30), "Back")) GoHome();
string msg = current != null ? "Right click, Esc or scroll out to go back"
: hoverClick != null ? hoverClick.Hint
: hover != null ? hover.label
: "Click the tank or the computer to take a closer look";
GUI.Label(new Rect(14, 10, 800, 26), msg, style);
}
}