Files
MVP1/Assets/Scripts/WorldMap/MapCameraController.cs

152 lines
5.3 KiB
C#

using UnityEngine;
using UnityEngine.EventSystems;
// Камера карты мира: перетаскивание (палец/мышь), зум (два пальца/колёсико), границы карты.
// Отличает "тап" от "перетаскивания": тап -- это касание, при котором палец почти не сдвинулся.
[DefaultExecutionOrder(-10)] // считать тап раньше, чем его прочитает WorldMapController
[RequireComponent(typeof(Camera))]
public class MapCameraController : MonoBehaviour
{
[Header("Зум")]
public float minOrthoSize = 4f;
public float maxOrthoSize = 16f;
public float mouseZoomSpeed = 2f;
[Header("Тап")]
[Tooltip("Если палец сдвинулся меньше, чем на столько пикселей, -- это нажатие, а не перетаскивание")]
public float tapMaxMovePixels = 15f;
// Границы карты в клетках Unity (задаёт WorldMapController)
[HideInInspector] public Vector2 mapHalfSize = new Vector2(20f, 14f);
private Camera cam;
private Vector3 lastPointerPosition;
private Vector3 pressPosition;
private bool isPressed;
private bool isDragging;
private bool pressStartedOverUI;
private bool hasTap;
private Vector3 tapWorldPosition;
void Awake()
{
cam = GetComponent<Camera>();
}
// Было ли за этот кадр нажатие по карте (не по UI и не перетаскивание)
public bool TryGetTap(out Vector3 worldPosition)
{
worldPosition = tapWorldPosition;
return hasTap;
}
public void CenterOn(Vector3 worldPosition)
{
transform.position = new Vector3(worldPosition.x, worldPosition.y, transform.position.z);
ClampToMap();
}
void Update()
{
hasTap = false;
if (Input.touchCount >= 2)
{
HandlePinch();
isPressed = false; // после щипка не засчитываем отпускание как тап
return;
}
HandlePointer();
HandleMouseWheel();
ClampToMap();
}
void HandlePointer()
{
if (Input.GetMouseButtonDown(0))
{
isPressed = true;
isDragging = false;
pressStartedOverUI = IsPointerOverUI();
pressPosition = Input.mousePosition;
lastPointerPosition = Input.mousePosition;
}
if (!isPressed || pressStartedOverUI)
{
if (Input.GetMouseButtonUp(0)) isPressed = false;
return;
}
if (Input.GetMouseButton(0))
{
if (!isDragging && (Input.mousePosition - pressPosition).magnitude > tapMaxMovePixels)
isDragging = true;
if (isDragging)
{
// Сдвигаем камеру ровно на столько, на сколько сдвинулся палец в мире -- карта "прилипает" к пальцу
Vector3 previous = cam.ScreenToWorldPoint(lastPointerPosition);
Vector3 current = cam.ScreenToWorldPoint(Input.mousePosition);
transform.position += previous - current;
}
lastPointerPosition = Input.mousePosition;
}
if (Input.GetMouseButtonUp(0))
{
if (!isDragging)
{
hasTap = true;
tapWorldPosition = cam.ScreenToWorldPoint(Input.mousePosition);
tapWorldPosition.z = 0f;
}
isPressed = false;
isDragging = false;
}
}
void HandlePinch()
{
Touch a = Input.GetTouch(0);
Touch b = Input.GetTouch(1);
float previousDistance = ((a.position - a.deltaPosition) - (b.position - b.deltaPosition)).magnitude;
float currentDistance = (a.position - b.position).magnitude;
if (currentDistance <= 0.01f) return;
cam.orthographicSize = Mathf.Clamp(cam.orthographicSize * previousDistance / currentDistance, minOrthoSize, maxOrthoSize);
ClampToMap();
}
void HandleMouseWheel()
{
float scroll = Input.GetAxis("Mouse ScrollWheel");
if (scroll == 0f || IsPointerOverUI()) return;
cam.orthographicSize = Mathf.Clamp(cam.orthographicSize - scroll * mouseZoomSpeed * 5f, minOrthoSize, maxOrthoSize);
}
// Камера не уезжает за край карты (если карта меньше экрана -- центрируем)
void ClampToMap()
{
float halfViewHeight = cam.orthographicSize;
float halfViewWidth = halfViewHeight * cam.aspect;
float maxX = Mathf.Max(0f, mapHalfSize.x - halfViewWidth);
float maxY = Mathf.Max(0f, mapHalfSize.y - halfViewHeight);
Vector3 p = transform.position;
p.x = Mathf.Clamp(p.x, -maxX, maxX);
p.y = Mathf.Clamp(p.y, -maxY, maxY);
transform.position = p;
}
static bool IsPointerOverUI()
{
if (EventSystem.current == null) return false;
if (Input.touchCount > 0) return EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId);
return EventSystem.current.IsPointerOverGameObject();
}
}