using UnityEngine; public class CameraController : MonoBehaviour { [Header("Настройки позиции камеры (Зум)")] public float distance = 10f; public float minDistance = 3f; public float maxDistance = 30f; public float zoomSpeed = 0.05f; [Header("Настройки цели (Ось Y)")] public float targetY = 0f; public float minTargetY = 0f; public float maxTargetY = 15f; public float moveTargetSpeed = 0.02f; public float addingHeight = 3f; [Header("Настройки вращения")] public float rotationSpeed = 0.2f; [SerializeField] private float currentAngle = -45f; [Header("Настройки смещения (Pan - НОВОЕ)")] [Tooltip("Скорость смещения камеры по горизонтали")] public float panSpeed = 0.03f; [Tooltip("При каком отдалении камеры разрешено смещение? (Если отдалить дальше - смещение сбросится)")] public float zoomThresholdForPan = 15f; [Tooltip("Максимальное смещение от центра комнаты")] public float maxPanDistance = 10f; [Header("Настройки плавности (Сглаживание)")] [Range(0.01f, 0.5f)] public float smoothTime = 0.1f; public float touchDeadZone = 1.5f; // ЦЕЛЕВЫЕ значения private float _targetAngle; private float _targetY; private float _targetDistance; // НОВОЕ: Целевое и текущее смещение по осям X и Z private Vector2 _targetPanOffset; private Vector2 _currentPanOffset; // Внутренние переменные скорости для SmoothDamp private float _angleVelocity; private float _yVelocity; private float _distanceVelocity; private Vector2 _panVelocity; // Скорость для смещения void Start() { _targetAngle = currentAngle; _targetY = targetY; _targetDistance = distance; _targetPanOffset = Vector2.zero; _currentPanOffset = Vector2.zero; UpdateCameraPosition(); } void LateUpdate() { HandleInput(); CheckPanReset(); // Проверяем, не пора ли сбросить смещение SmoothMovement(); UpdateCameraPosition(); } private void HandleInput() { // === 1. УПРАВЛЕНИЕ ОДНИМ ПАЛЬЦЕМ (Вращение и высота) === if (Input.touchCount == 1) { Touch touch = Input.GetTouch(0); if (touch.phase == TouchPhase.Moved && touch.deltaPosition.sqrMagnitude > touchDeadZone * touchDeadZone) { _targetAngle += touch.deltaPosition.x * rotationSpeed; _targetY -= touch.deltaPosition.y * moveTargetSpeed; _targetY = Mathf.Clamp(_targetY, minTargetY, maxTargetY); } } // === 2. УПРАВЛЕНИЕ ДВУМЯ ПАЛЬЦАМИ (Зум и Смещение) === else if (Input.touchCount == 2) { Touch touch0 = Input.GetTouch(0); Touch touch1 = Input.GetTouch(1); // -- Логика Зума -- Vector2 touch0PrevPos = touch0.position - touch0.deltaPosition; Vector2 touch1PrevPos = touch1.position - touch1.deltaPosition; float prevMagnitude = (touch0PrevPos - touch1PrevPos).magnitude; float currentMagnitude = (touch0.position - touch1.position).magnitude; float difference = currentMagnitude - prevMagnitude; if (Mathf.Abs(difference) > touchDeadZone) { _targetDistance -= difference * zoomSpeed; _targetDistance = Mathf.Clamp(_targetDistance, minDistance, maxDistance); } // -- НОВОЕ: Логика смещения (Панорамирование) -- // Разрешаем смещение только если камера достаточно близко if (_targetDistance <= zoomThresholdForPan) { // Берем среднее движение двух пальцев Vector2 avgDelta = (touch0.deltaPosition + touch1.deltaPosition) / 2f; ApplyPan(avgDelta.x, avgDelta.y); } } // === 3. МЫШЬ (Для редактора ПК) === else { // Левая кнопка - Вращение и высота if (Input.GetMouseButton(0)) { _targetAngle += Input.GetAxis("Mouse X") * rotationSpeed * 20f; _targetY -= Input.GetAxis("Mouse Y") * moveTargetSpeed * 20f; _targetY = Mathf.Clamp(_targetY, minTargetY, maxTargetY); } // НОВОЕ: Правая кнопка - Смещение (Pan) else if (Input.GetMouseButton(1) && _targetDistance <= zoomThresholdForPan) { ApplyPan(Input.GetAxis("Mouse X") * 20f, Input.GetAxis("Mouse Y") * 20f); } // Зум колесиком мыши float scroll = Input.GetAxis("Mouse ScrollWheel"); if (scroll != 0) { _targetDistance -= scroll * zoomSpeed * 100f; _targetDistance = Mathf.Clamp(_targetDistance, minDistance, maxDistance); } } } // НОВЫЙ МЕТОД: Применение смещения относительно текущего поворота камеры private void ApplyPan(float deltaX, float deltaY) { // Высчитываем векторы "вправо" и "вперед" для камеры, игнорируя наклон (ось Y) Vector3 right = transform.right; right.y = 0; right.Normalize(); Vector3 forward = transform.forward; forward.y = 0; forward.Normalize(); // Умножаем движение пальца/мыши на направление камеры Vector3 move = (right * -deltaX) + (forward * -deltaY); _targetPanOffset += new Vector2(move.x, move.z) * panSpeed; // Ограничиваем максимальное смещение, чтобы не улететь за карту if (_targetPanOffset.magnitude > maxPanDistance) { _targetPanOffset = _targetPanOffset.normalized * maxPanDistance; } } // НОВЫЙ МЕТОД: Сброс смещения при отдалении private void CheckPanReset() { if (_targetDistance > zoomThresholdForPan) { // Если отдалились слишком сильно - обнуляем целевое смещение. // Благодаря SmoothDamp камера вернется в центр плавно! _targetPanOffset = Vector2.zero; } } private void SmoothMovement() { currentAngle = Mathf.SmoothDamp(currentAngle, _targetAngle, ref _angleVelocity, smoothTime); targetY = Mathf.SmoothDamp(targetY, _targetY, ref _yVelocity, smoothTime); distance = Mathf.SmoothDamp(distance, _targetDistance, ref _distanceVelocity, smoothTime); // НОВОЕ: Плавно сглаживаем позицию смещения _currentPanOffset = Vector2.SmoothDamp(_currentPanOffset, _targetPanOffset, ref _panVelocity, smoothTime); } private void UpdateCameraPosition() { // НОВОЕ: Теперь цель вращения учитывает наше смещение по осям X и Z Vector3 targetPoint = new Vector3(_currentPanOffset.x, targetY, _currentPanOffset.y); float radians = currentAngle * Mathf.Deg2Rad; float x = targetPoint.x + distance * Mathf.Sin(radians); float z = targetPoint.z + distance * Mathf.Cos(radians); transform.position = new Vector3(x, targetY + addingHeight, z); transform.LookAt(targetPoint); } }