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(); } // Было ли за этот кадр нажатие по карте (не по 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(); } }