| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- using UnityEngine;
- namespace MazeGen
- {
- /// <summary>
- /// Top-down camera rig: smoothly glides to focus on newly entered rooms, and can be
- /// freely panned with WASD or by holding the middle mouse button and dragging. Any
- /// manual pan input cancels whatever auto-focus is currently in progress.
- /// </summary>
- public class MazeCameraController : MonoBehaviour
- {
- [SerializeField] private float panSpeed = 8f;
- [SerializeField] private float focusSharpness = 6f;
- [SerializeField] private float dragSensitivity = 1f;
- private Camera _camera;
- private Vector2? _focusTarget;
- private Vector2 _lastPointerScreenPos;
- private bool _isDragging;
- private void Awake()
- {
- _camera = GetComponent<Camera>();
- if (_camera == null) _camera = Camera.main;
- }
- /// <summary>Smoothly glides the camera to look at worldPosition.</summary>
- public void FocusOn(Vector2 worldPosition)
- {
- _focusTarget = worldPosition;
- }
- /// <summary>Instantly moves the camera to worldPosition, no smoothing.</summary>
- public void SnapTo(Vector2 worldPosition)
- {
- _focusTarget = null;
- transform.position = new Vector3(worldPosition.x, worldPosition.y, transform.position.z);
- }
- private void Update()
- {
- HandleDrag();
- HandleKeyboardPan();
- HandleAutoFocus();
- }
- private void HandleKeyboardPan()
- {
- Vector2 input = MazeInput.ReadWasdAxis();
- if (input.sqrMagnitude <= 0f) return;
- _focusTarget = null; // manual input takes over from auto-focus
- transform.position += new Vector3(input.x, input.y, 0f) * (panSpeed * Time.deltaTime);
- }
- private void HandleDrag()
- {
- bool middleHeldNow = MazeInput.IsMiddleMouseHeld();
- Vector2 pointerNow = MazeInput.GetPointerScreenPosition();
- if (middleHeldNow && !_isDragging)
- {
- _isDragging = true;
- _lastPointerScreenPos = pointerNow;
- return;
- }
- if (middleHeldNow)
- {
- Vector2 screenDelta = pointerNow - _lastPointerScreenPos;
- if (screenDelta.sqrMagnitude > 0f)
- {
- _focusTarget = null; // manual input takes over from auto-focus
- Vector2 worldDelta = ScreenDeltaToWorldDelta(screenDelta);
- transform.position -= new Vector3(worldDelta.x, worldDelta.y, 0f);
- }
- _lastPointerScreenPos = pointerNow;
- }
- else
- {
- _isDragging = false;
- }
- }
- private void HandleAutoFocus()
- {
- if (!_focusTarget.HasValue) return;
- Vector2 current = transform.position;
- Vector2 target = _focusTarget.Value;
- // Framerate-independent exponential smoothing rather than a constant-speed move,
- // so the glide eases in and settles instead of arriving abruptly.
- float t = 1f - Mathf.Exp(-focusSharpness * Time.deltaTime);
- Vector2 next = Vector2.Lerp(current, target, t);
- transform.position = new Vector3(next.x, next.y, transform.position.z);
- if (Vector2.Distance(next, target) < 0.01f)
- _focusTarget = null;
- }
- private Vector2 ScreenDeltaToWorldDelta(Vector2 screenDelta)
- {
- if (_camera == null || Screen.height <= 0) return Vector2.zero;
- float worldUnitsPerPixel;
- if (_camera.orthographic)
- {
- worldUnitsPerPixel = (_camera.orthographicSize * 2f) / Screen.height;
- }
- else
- {
- // Rooms sit on the z = 0 plane; use the camera's distance to it as the
- // reference plane for converting a screen-space drag into world units.
- float distance = Mathf.Abs(transform.position.z);
- worldUnitsPerPixel = (2f * distance * Mathf.Tan(_camera.fieldOfView * 0.5f * Mathf.Deg2Rad)) / Screen.height;
- }
- return screenDelta * worldUnitsPerPixel * dragSensitivity;
- }
- }
- }
|