Zabiju je
This commit is contained in:
216
Assets/Scripts/MapCameraController.cs
Normal file
216
Assets/Scripts/MapCameraController.cs
Normal file
@@ -0,0 +1,216 @@
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Attach to Main Camera in Client.unity.
|
||||
/// Top-down perspective camera that follows the local player capsule.
|
||||
///
|
||||
/// Features:
|
||||
/// • Auto-follow player when tracking (can be paused by dragging)
|
||||
/// • Single-finger touch drag (or mouse drag) to pan
|
||||
/// • Pinch gesture (or mouse scroll wheel) to zoom (changes camera height)
|
||||
/// • Double-tap anywhere to instantly recenter on player
|
||||
/// • Static Recenter() method called by the HUD recenter button
|
||||
/// </summary>
|
||||
public class MapCameraController : MonoBehaviour
|
||||
{
|
||||
// ── Singleton (weak — no DontDestroyOnLoad needed, camera lives in Client.unity) ──
|
||||
public static MapCameraController Instance { get; private set; }
|
||||
|
||||
// ── Public API ────────────────────────────────────────────────────────────
|
||||
public void SetTarget(GameObject target) { _target = target; }
|
||||
public void Recenter() { _isTracking = true; _resumeTimer = 0f; }
|
||||
|
||||
// ── Tuning ────────────────────────────────────────────────────────────────
|
||||
private const float FollowSmoothing = 8f; // lerp speed when tracking
|
||||
private const float DefaultHeight = 150f; // camera Y (metres above ground)
|
||||
private const float MinHeight = 30f; // closest zoom
|
||||
private const float MaxHeight = 350f; // furthest zoom
|
||||
private const float PinchZoomSens = 1.2f; // multiplier for pinch speed
|
||||
private const float ScrollZoomSens = 30f; // world-units per scroll tick
|
||||
private const float ResumeDelay = 3.5f; // s after drag ends before auto-tracking resumes
|
||||
private const float DoubleTapWindow = 0.32f; // s between taps to count as double
|
||||
private const float DragThreshold = 8f; // pixels moved before drag starts
|
||||
|
||||
// ── Runtime state ─────────────────────────────────────────────────────────
|
||||
private Camera _cam;
|
||||
private GameObject _target;
|
||||
private float _currentHeight;
|
||||
private bool _isTracking = true;
|
||||
private float _resumeTimer;
|
||||
|
||||
// Drag
|
||||
private bool _dragActive;
|
||||
private Vector2 _lastDragScreen;
|
||||
|
||||
// Pinch
|
||||
private float _pinchStartDist = -1f;
|
||||
private float _pinchStartHeight;
|
||||
|
||||
// Double-tap
|
||||
private int _tapCount;
|
||||
private float _tapTimer;
|
||||
|
||||
// ── MonoBehaviour ─────────────────────────────────────────────────────────
|
||||
void Awake()
|
||||
{
|
||||
Instance = this;
|
||||
_cam = GetComponent<Camera>();
|
||||
if (_cam == null) { Debug.LogError("[MapCamera] No Camera component!"); return; }
|
||||
|
||||
// Keep existing perspective mode — just ensure straight-down orientation
|
||||
transform.rotation = Quaternion.Euler(90f, 0f, 0f);
|
||||
_currentHeight = transform.position.y > 1f ? transform.position.y : DefaultHeight;
|
||||
transform.position = new Vector3(transform.position.x, _currentHeight, transform.position.z);
|
||||
}
|
||||
|
||||
void OnEnable() { Instance = this; }
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
HandleInput();
|
||||
FollowTarget();
|
||||
}
|
||||
|
||||
// ── Target following ──────────────────────────────────────────────────────
|
||||
void FollowTarget()
|
||||
{
|
||||
if (!_isTracking || _target == null) return;
|
||||
Vector3 tp = _target.transform.position;
|
||||
Vector3 dest = new Vector3(tp.x, _currentHeight, tp.z);
|
||||
transform.position = Vector3.Lerp(transform.position, dest, Time.deltaTime * FollowSmoothing);
|
||||
}
|
||||
|
||||
// ── Input ─────────────────────────────────────────────────────────────────
|
||||
void HandleInput()
|
||||
{
|
||||
// Auto-resume tracking after a period of no dragging
|
||||
if (!_isTracking)
|
||||
{
|
||||
_resumeTimer += Time.deltaTime;
|
||||
if (_resumeTimer >= ResumeDelay) _isTracking = true;
|
||||
}
|
||||
|
||||
// Double-tap timer
|
||||
_tapTimer += Time.deltaTime;
|
||||
if (_tapTimer > DoubleTapWindow) _tapCount = 0;
|
||||
|
||||
int tc = Input.touchCount;
|
||||
|
||||
if (tc == 2)
|
||||
{
|
||||
HandlePinch();
|
||||
return;
|
||||
}
|
||||
|
||||
_pinchStartDist = -1f; // reset pinch when not 2 fingers
|
||||
|
||||
if (tc == 1)
|
||||
{
|
||||
Touch t = Input.GetTouch(0);
|
||||
switch (t.phase)
|
||||
{
|
||||
case TouchPhase.Began:
|
||||
OnPointerDown(t.position);
|
||||
break;
|
||||
case TouchPhase.Moved:
|
||||
case TouchPhase.Stationary:
|
||||
OnPointerDrag(t.position);
|
||||
break;
|
||||
case TouchPhase.Ended:
|
||||
case TouchPhase.Canceled:
|
||||
OnPointerUp();
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Mouse fallback (editor / desktop)
|
||||
if (Input.GetMouseButtonDown(0)) OnPointerDown(Input.mousePosition);
|
||||
else if (Input.GetMouseButton(0)) OnPointerDrag(Input.mousePosition);
|
||||
else if (Input.GetMouseButtonUp(0)) OnPointerUp();
|
||||
|
||||
float scroll = Input.GetAxis("Mouse ScrollWheel");
|
||||
if (Mathf.Abs(scroll) > 0.001f)
|
||||
{
|
||||
_currentHeight = Mathf.Clamp(_currentHeight - scroll * ScrollZoomSens, MinHeight, MaxHeight);
|
||||
transform.position = new Vector3(transform.position.x, _currentHeight, transform.position.z);
|
||||
}
|
||||
}
|
||||
|
||||
void OnPointerDown(Vector2 screenPos)
|
||||
{
|
||||
_lastDragScreen = screenPos;
|
||||
_dragActive = false;
|
||||
|
||||
// Double-tap detection
|
||||
_tapCount++;
|
||||
_tapTimer = 0f;
|
||||
if (_tapCount >= 2)
|
||||
{
|
||||
_tapCount = 0;
|
||||
Recenter();
|
||||
}
|
||||
}
|
||||
|
||||
void OnPointerDrag(Vector2 screenPos)
|
||||
{
|
||||
Vector2 screenDelta = screenPos - _lastDragScreen;
|
||||
|
||||
if (!_dragActive && screenDelta.magnitude > DragThreshold)
|
||||
{
|
||||
_dragActive = true;
|
||||
_isTracking = false;
|
||||
_resumeTimer = 0f;
|
||||
}
|
||||
|
||||
if (_dragActive)
|
||||
{
|
||||
// Pan: move camera so that the world point under the finger stays fixed.
|
||||
// Because the camera faces straight down, we can use a simpler formula:
|
||||
// pixels → world = (camera height / focal length in pixels) ratio.
|
||||
// For perspective: visible half-height at ground = height * tan(fov/2)
|
||||
// world_per_pixel = 2 * height * tan(fov/2) / screenHeight
|
||||
float halfFovRad = _cam.fieldOfView * 0.5f * Mathf.Deg2Rad;
|
||||
float worldPerPixelY = 2f * _currentHeight * Mathf.Tan(halfFovRad) / Screen.height;
|
||||
float worldPerPixelX = worldPerPixelY * ((float)Screen.width / Screen.height);
|
||||
|
||||
// Flip: dragging right moves world right (camera moves left)
|
||||
transform.position += new Vector3(
|
||||
-screenDelta.x * worldPerPixelX,
|
||||
0f,
|
||||
-screenDelta.y * worldPerPixelY
|
||||
);
|
||||
}
|
||||
|
||||
_lastDragScreen = screenPos;
|
||||
}
|
||||
|
||||
void OnPointerUp()
|
||||
{
|
||||
_dragActive = false;
|
||||
}
|
||||
|
||||
// ── Pinch zoom ────────────────────────────────────────────────────────────
|
||||
void HandlePinch()
|
||||
{
|
||||
Touch t0 = Input.GetTouch(0);
|
||||
Touch t1 = Input.GetTouch(1);
|
||||
|
||||
if (t0.phase == TouchPhase.Began || t1.phase == TouchPhase.Began)
|
||||
{
|
||||
_pinchStartDist = Vector2.Distance(t0.position, t1.position);
|
||||
_pinchStartHeight = _currentHeight;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_pinchStartDist <= 0f) return;
|
||||
|
||||
float currentDist = Vector2.Distance(t0.position, t1.position);
|
||||
if (currentDist < 1f) return;
|
||||
|
||||
// Closer fingers = zoom in (lower height)
|
||||
float ratio = _pinchStartDist / currentDist;
|
||||
_currentHeight = Mathf.Clamp(_pinchStartHeight * ratio * PinchZoomSens, MinHeight, MaxHeight);
|
||||
transform.position = new Vector3(transform.position.x, _currentHeight, transform.position.z);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user