using System; // For Action event using UnityEngine; namespace OnlyScove.Scripts { public class CameraController : MonoBehaviour { public enum CameraViewMode { ThirdPerson, FirstPerson } [SerializeField] InputReader inputReader; [SerializeField] Transform followTarget; // Player's root for TPV [SerializeField] float positionSmoothTime = 0.12f; [SerializeField] float rotationSmoothTime = 5f; [SerializeField] Vector2 framingOffset; [Header("Components")] [SerializeField] private CameraRotationHandler rotationHandler = new CameraRotationHandler(); [SerializeField] private CameraZoomHandler zoomHandler = new CameraZoomHandler(); [SerializeField] private CameraCollisionHandler collisionHandler = new CameraCollisionHandler(); [SerializeField] private CameraOcclusionTransparency occlusionTransparency = new CameraOcclusionTransparency(); [SerializeField] private CameraDynamicFOV dynamicFOV = new CameraDynamicFOV(); [SerializeField] private CameraCharacterFading characterFading = new CameraCharacterFading(); [SerializeField] private CameraSideBias sideBias = new CameraSideBias(); [SerializeField] private CameraShakeManager shakeManager = new CameraShakeManager(); [Header("First Person View Settings")] [SerializeField] Transform fpvTarget; // Specific transform on the player (e.g., eye level) [SerializeField] float fpvPositionSmoothTime = 0.05f; [SerializeField] float fpvRotationSmoothTime = 0.05f; [SerializeField] float fpvFOV = 80f; [SerializeField] float transitionDuration = 0.3f; [SerializeField] float tpvBaseFOV = 60f; // Existing base FOV for TPV private Vector3 _currentVelocity; private Camera _cam; private CameraViewMode _currentViewMode = CameraViewMode.ThirdPerson; private CameraViewMode _targetViewMode = CameraViewMode.ThirdPerson; private float _transitionTimer = 0f; private bool _inTransition = false; // Properties to get current smoothing values based on view mode private float CurrentPositionSmoothTime => _currentViewMode == CameraViewMode.FirstPerson ? fpvPositionSmoothTime : positionSmoothTime; private float CurrentRotationSmoothTime => _currentViewMode == CameraViewMode.FirstPerson ? fpvRotationSmoothTime : rotationSmoothTime; private void OnEnable() { if (inputReader != null) { inputReader.OnToggleViewEvent += ToggleCameraView; } } private void OnDisable() { if (inputReader != null) { inputReader.OnToggleViewEvent -= ToggleCameraView; } } private void Start() { _cam = GetComponent(); Cursor.visible = false; Cursor.lockState = CursorLockMode.Locked; rotationHandler.Initialize(transform); dynamicFOV.Initialize(tpvBaseFOV: tpvBaseFOV, fpvFOV: fpvFOV); // Pass TPV and FPV base FOVs } private void Update() { HandleViewTransition(); if (inputReader != null) { // Input-related updates are handled differently based on view mode rotationHandler.HandleRotation(inputReader, followTarget, CurrentRotationSmoothTime, _currentViewMode); if (_currentViewMode == CameraViewMode.ThirdPerson) { zoomHandler.HandleZoom(inputReader); sideBias.HandleSideBias(inputReader); } else { // Disable side bias and zoom in FPV sideBias.HandleSideBias(null); // Pass null to effectively disable zoomHandler.HandleZoom(null); // Pass null to effectively disable } dynamicFOV.HandleDynamicFOV(_cam, inputReader, _currentViewMode); } Vector3 focusPosition; float targetDistance; if (_currentViewMode == CameraViewMode.ThirdPerson) { // TPV specific calculations transform.rotation = rotationHandler.CurrentRotation; // Set rotation from handler focusPosition = followTarget.position + rotationHandler.CurrentRotation * new Vector3(framingOffset.x + sideBias.CurrentSideBias, framingOffset.y, 0); targetDistance = collisionHandler.CheckCollision(focusPosition, rotationHandler.CurrentRotation, zoomHandler.CurrentDistance, zoomHandler.MinDistance); characterFading.HandleCharacterFading(targetDistance); occlusionTransparency.HandleTransparency(transform, focusPosition); } else // FirstPerson { // FPV specific calculations // Rotation is derived from fpvTarget, not input from rotationHandler transform.rotation = fpvTarget.rotation; // Directly set FPV rotation focusPosition = fpvTarget.position; targetDistance = 0; // FPV has no distance to player // Disable TPV-specific effects characterFading.HandleCharacterFading(0); // Fully opaque character in FPV occlusionTransparency.HandleTransparency(transform, fpvTarget.position); // Can still have occlusion transparency for environment in FPV } // Calculate target position using the currently set transform.rotation Vector3 targetPosition = focusPosition - transform.rotation * new Vector3(0, 0, targetDistance); // Handle camera shake shakeManager.HandleShake(); // Apply final position and rotation transform.position = Vector3.SmoothDamp(transform.position, targetPosition, ref _currentVelocity, CurrentPositionSmoothTime) + shakeManager.ShakeOffset; } private void ToggleCameraView() { if (_inTransition) return; // Prevent multiple toggles during transition _targetViewMode = (_currentViewMode == CameraViewMode.ThirdPerson) ? CameraViewMode.FirstPerson : CameraViewMode.ThirdPerson; _inTransition = true; _transitionTimer = 0f; } private void HandleViewTransition() { if (!_inTransition) return; _transitionTimer += Time.deltaTime; float t = _transitionTimer / transitionDuration; t = Mathf.Clamp01(t); // Clamp t between 0 and 1 // Smoothly interpolate parameters during transition if (_currentViewMode == CameraViewMode.ThirdPerson && _targetViewMode == CameraViewMode.FirstPerson) { // TPV -> FPV transition // Interpolate FOV _cam.fieldOfView = Mathf.Lerp(dynamicFOV.CurrentTpvBaseFOV, fpvFOV, t); // Interpolate position and rotation transform.position = Vector3.Lerp(transform.position, fpvTarget.position, t); transform.rotation = Quaternion.Slerp(transform.rotation, fpvTarget.rotation, t); } else if (_currentViewMode == CameraViewMode.FirstPerson && _targetViewMode == CameraViewMode.ThirdPerson) { // FPV -> TPV transition // Interpolate FOV _cam.fieldOfView = Mathf.Lerp(fpvFOV, dynamicFOV.CurrentTpvBaseFOV, t); // For position and rotation, we'll let the TPV logic take over after transition. // If a smooth exit from FPV to TPV is desired for position/rotation, // more complex calculations would be needed here to store TPV target position/rotation at the start. } if (t >= 1f) { _currentViewMode = _targetViewMode; _inTransition = false; // Re-initialize rotation handler for new view mode context rotationHandler.Initialize(transform); // Ensure FOV is set correctly at the end of transition _cam.fieldOfView = (_currentViewMode == CameraViewMode.FirstPerson) ? fpvFOV : dynamicFOV.CurrentTpvBaseFOV; // For FPV, ensure camera matches fpvTarget immediately after transition // This is already handled by the `transform.position = Vector3.SmoothDamp...` and `transform.rotation = fpvTarget.rotation` in Update } } public void Shake(float intensity, float duration) { shakeManager.Shake(intensity, duration); } public void TriggerFallImpactShake(float fallHeight) { shakeManager.TriggerFallImpactShake(fallHeight); } public Quaternion PlanarRotation => rotationHandler.PlanarRotation; } }