152 lines
5.3 KiB
C#
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();
|
||
|
|
}
|
||
|
|
}
|