215 lines
11 KiB
C#
215 lines
11 KiB
C#
using System;
|
||
using Code.Configs;
|
||
using Code.Gameplay.Camera;
|
||
using Code.Gameplay.Character.Core;
|
||
using Code.StaticData;
|
||
using Reflex.Attributes;
|
||
using UnityEngine;
|
||
|
||
namespace Code.Gameplay.Character.Camera
|
||
{
|
||
/*
|
||
* Скрипт, отвечающий за поведение камеры при передвижении
|
||
* Поддерживает как 3rd-person, так и 1st-person режимы камеры
|
||
* Реализует плавное следование за персонажем, вращение и зум камеры
|
||
*
|
||
* Источник: Tvtig, https://www.youtube.com/watch?v=9ibpY64wBYM
|
||
*/
|
||
public class CameraService : MonoBehaviour
|
||
{
|
||
// Параметры загружаются из CameraConfig
|
||
private float
|
||
_defaultDistance,
|
||
_minDistance,
|
||
_maxDistance,
|
||
_distanceMovementSpeed,
|
||
_rotationSpeed,
|
||
_rotationSharpness,
|
||
_followSharpness,
|
||
_minVerticalAngle,
|
||
_maxVerticalAngle,
|
||
_defaultVerticalAngle;
|
||
|
||
// Трансформ объекта, за которым следует камера (для 1st-person: уровень глаз)
|
||
private Transform _followTransform;
|
||
|
||
// Позиция следования: текущая сглаженная позиция и горизонтальное направление камеры
|
||
private Vector3 _currentFollowPosition;
|
||
|
||
// Fallback-позиция для режимов без цели (например, MainMenu)
|
||
private Vector3 _defaultFollowPosition;
|
||
|
||
// Целевой вертикальный угол камеры (наклон вверх/вниз)
|
||
private float _targetVerticalAngle;
|
||
|
||
// Текущая и целевая дистанция камеры от цели
|
||
private float _currentDistance, _targetDistance;
|
||
|
||
// Исходные значения дистанции для восстановления после FirstPerson режима
|
||
private float _originalDefaultDistance, _originalMinDistance, _originalMaxDistance;
|
||
|
||
// Горизонтальное направление камеры (проекция forward на плоскость)
|
||
public Vector3 PlanarDirection { get; private set; }
|
||
|
||
// Текущий режим работы камеры
|
||
public CameraMode CurrentMode { get; private set; }
|
||
|
||
// Загрузка конфига для камеры
|
||
[Inject]
|
||
private void LoadConfig(CameraConfig config)
|
||
{
|
||
// Загрузка значений из конфига
|
||
|
||
_defaultDistance = config.defaultDistance;
|
||
_minDistance = config.minDistance;
|
||
_maxDistance = config.maxDistance;
|
||
_distanceMovementSpeed = config.distanceMovementSpeed;
|
||
_rotationSpeed = config.rotationSpeed;
|
||
_rotationSharpness = config.rotationSharpness;
|
||
_followSharpness = config.followSharpness;
|
||
_minVerticalAngle = config.minVerticalAngle;
|
||
_maxVerticalAngle = config.maxVerticalAngle;
|
||
_defaultVerticalAngle = config.defaultVerticalAngle;
|
||
CurrentMode = config.defaultCameraMode;
|
||
_defaultFollowPosition = config.defaultFollowPosition;
|
||
|
||
// Валидация значений (на случай ручного редактирования конфига)
|
||
_defaultDistance = Mathf.Clamp(_defaultDistance, _minDistance, _maxDistance);
|
||
_defaultVerticalAngle = Mathf.Clamp(_defaultVerticalAngle, _minVerticalAngle, _maxVerticalAngle);
|
||
|
||
// Сохраняем оригинальные значения для восстановления
|
||
_originalDefaultDistance = _defaultDistance;
|
||
_originalMinDistance = _minDistance;
|
||
_originalMaxDistance = _maxDistance;
|
||
|
||
// Инициализация состояния камеры
|
||
_currentDistance = _defaultDistance;
|
||
_targetDistance = _currentDistance;
|
||
_targetVerticalAngle = _defaultVerticalAngle;
|
||
PlanarDirection = Vector3.forward; // Начальное направление - вперед по оси Z
|
||
_currentFollowPosition = _defaultFollowPosition;
|
||
}
|
||
|
||
|
||
// Установка цели для следования камеры
|
||
public void SetFollowTransform(Transform t)
|
||
{
|
||
_followTransform = t;
|
||
_currentFollowPosition = t.position;
|
||
PlanarDirection = t.forward; // Начальное направление совпадает с направлением цели
|
||
}
|
||
|
||
// Обработка вращения камеры на основе пользовательского ввода
|
||
private void HandleRotationInput(float deltaTime, Vector3 rotationInput, out Quaternion targetRotation)
|
||
{
|
||
// Вращение вокруг вертикальной оси (горизонтальный поворот)
|
||
var rotationFromInput = Quaternion.Euler(_followTransform.up * (rotationInput.x * _rotationSpeed));
|
||
PlanarDirection = rotationFromInput * PlanarDirection;
|
||
var planarRotation = Quaternion.LookRotation(PlanarDirection, _followTransform.up);
|
||
|
||
// Вращение вокруг горизонтальной оси (вертикальный наклон)
|
||
_targetVerticalAngle -= rotationInput.y * _rotationSpeed;
|
||
_targetVerticalAngle = Math.Clamp(_targetVerticalAngle, _minVerticalAngle, _maxVerticalAngle);
|
||
var verticalRotation = Quaternion.Euler(_targetVerticalAngle, 0, 0);
|
||
|
||
// Интерполяция к целевому вращению для плавности
|
||
targetRotation = Quaternion.Slerp(transform.rotation, planarRotation * verticalRotation, deltaTime * _rotationSharpness);
|
||
|
||
// Применение финального вращения
|
||
transform.rotation = targetRotation;
|
||
}
|
||
|
||
// Обработка позиционирования камеры и зума с учетом текущего режима
|
||
private void HandlePosition(float deltaTime, float zoomInput, Quaternion targetRotation)
|
||
{
|
||
// В FirstPerson режиме зум отключается
|
||
if (CurrentMode != CameraMode.FirstPerson)
|
||
{
|
||
// Обновление целевой дистанции на основе ввода зума
|
||
_targetDistance += zoomInput * _distanceMovementSpeed;
|
||
_targetDistance = Mathf.Clamp(_targetDistance, _minDistance, _maxDistance);
|
||
}
|
||
|
||
// Плавное следование за целью с использованием экспоненциального сглаживания
|
||
_currentFollowPosition = Vector3.Lerp(_currentFollowPosition, _followTransform.position, 1f - Mathf.Exp(-_followSharpness * deltaTime));
|
||
|
||
// Вычисление целевой позиции камеры с учетом текущего режима
|
||
Vector3 targetPosition;
|
||
if (CurrentMode == CameraMode.FirstPerson)
|
||
{
|
||
// В FirstPerson режиме камера находится прямо на позиции цели
|
||
targetPosition = _currentFollowPosition;
|
||
}
|
||
else
|
||
{
|
||
// В ThirdPerson режиме камера отодвинута на расстояние от цели
|
||
targetPosition = _currentFollowPosition - targetRotation * Vector3.forward * _currentDistance;
|
||
}
|
||
|
||
// Плавное изменение текущей дистанции к целевой (только для ThirdPerson)
|
||
if (CurrentMode != CameraMode.FirstPerson)
|
||
{
|
||
_currentDistance = Mathf.Lerp(_currentDistance, _targetDistance, 1f - Mathf.Exp(-_distanceMovementSpeed * deltaTime));
|
||
}
|
||
|
||
// Установка финальной позиции камеры
|
||
transform.position = targetPosition;
|
||
}
|
||
|
||
// Основной метод обновления камеры, вызываемый извне каждый кадр
|
||
public void UpdateWithInput(float deltaTime, float zoomInput, Vector3 rotationInput)
|
||
{
|
||
// Проверка наличия цели для следования и проверка на режим главного меню
|
||
if (!_followTransform || CurrentMode == CameraMode.Bogdan) return;
|
||
|
||
// Обработка ввода и позиционирования
|
||
HandleRotationInput(deltaTime, rotationInput, out var targetRotation);
|
||
HandlePosition(deltaTime, zoomInput, targetRotation);
|
||
}
|
||
|
||
// Смена режима камеры
|
||
public void SetCameraMode(CameraMode newMode)
|
||
{
|
||
if (CurrentMode == newMode) return;
|
||
CurrentMode = newMode;
|
||
|
||
// Применяем настройки специфичные для режима
|
||
ApplyModeSpecificSettings();
|
||
}
|
||
|
||
// Применение настроек специфичных для текущего режима
|
||
private void ApplyModeSpecificSettings()
|
||
{
|
||
switch (CurrentMode)
|
||
{
|
||
case CameraMode.FirstPerson:
|
||
// В FirstPerson режиме дистанция всегда равна 0
|
||
_minDistance = 0f;
|
||
_maxDistance = 0f;
|
||
_defaultDistance = 0f;
|
||
_targetDistance = 0f;
|
||
_currentDistance = 0f;
|
||
break;
|
||
case CameraMode.ThirdPerson:
|
||
// Восстанавливаем исходные значения дистанции
|
||
_minDistance = _originalMinDistance;
|
||
_maxDistance = _originalMaxDistance;
|
||
_defaultDistance = _originalDefaultDistance;
|
||
_targetDistance = _defaultDistance;
|
||
break;
|
||
// Заготовка на будущее (если понадобится)
|
||
case CameraMode.Bogdan:
|
||
_minDistance = 0f;
|
||
_maxDistance = 0f;
|
||
_defaultDistance = 0f;
|
||
_targetDistance = 0f;
|
||
_currentDistance = 0f;
|
||
break;
|
||
case CameraMode.Static:
|
||
case CameraMode.Cinematic:
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
} |