using UnityEngine; using Unheimlich.Features.Player; namespace Unheimlich.World.HouseOfLeaves { /// /// Simulates a Dolly Zoom (Vertigo effect) by applying a speed-driven additive FOV offset /// on top of the base FOV already written by and /// earlier in the frame. /// /// Runs in LateUpdate — after both and /// — so it always layers on top of the final written value. /// Subtracts the previous frame's offset before adding the new one to avoid compounding. /// /// The faster the player moves, the more the FOV pinches inward, /// compressing the perceived corridor space and amplifying claustrophobia. /// public class DollyZoomEffect : MonoBehaviour { // ───────────────────────────────────────────────────────────────────────── // Serialized Fields // ───────────────────────────────────────────────────────────────────────── [Header("Effect")] [Tooltip("Enable or disable the effect at runtime.")] [SerializeField] private bool _isEnabled = true; [Tooltip("Maximum negative FOV offset at full speed (degrees). " + "Negative value pinches the FOV inward.")] [SerializeField] private float _maxFovOffset = -15f; [Tooltip("Walk speed (km/h) at which the effect starts fading in.")] [SerializeField] private float _speedThreshold = 12f; [Tooltip("Walk speed (km/h) at which the effect reaches its maximum.")] [SerializeField] private float _maxSpeedForEffect = 22f; [Tooltip("Lerp speed for the FOV offset transitions.")] [SerializeField] private float _smoothSpeed = 3f; [Header("Pulse (optional)")] [Tooltip("Adds a subtle sinusoidal oscillation to the FOV offset for extra unease.")] [SerializeField] private bool _enablePulse; [Tooltip("Amplitude of the pulse oscillation (degrees).")] [SerializeField] private float _pulseAmplitude = 1.5f; [Tooltip("Frequency of the pulse oscillation (Hz).")] [SerializeField] private float _pulseFrequency = 0.4f; // ───────────────────────────────────────────────────────────────────────── // Private Fields // ───────────────────────────────────────────────────────────────────────── private Camera _playerCamera; private float _currentFovOffset; private bool _isInitialized; private float _previousFovOffset; // ───────────────────────────────────────────────────────────────────────── // Unity Messages // ───────────────────────────────────────────────────────────────────────── private void Start() { if (PlayerController.Instance?.PlayerCamera == null) { Debug.LogError("[DollyZoomEffect] PlayerCamera could not be resolved."); return; } _playerCamera = PlayerController.Instance.PlayerCamera; _isInitialized = true; } // LateUpdate ensures this runs after HouseOfLeavesController and CameraEffects // have both written their FOV values in Update. private void LateUpdate() { if (!_isInitialized || !_isEnabled) return; TickDollyZoom(); } // ───────────────────────────────────────────────────────────────────────── // Tick // ───────────────────────────────────────────────────────────────────────── /// /// Computes the speed-driven FOV delta and applies it additively on top of /// the current fieldOfView. Strips the previous frame's offset first /// to avoid compounding the delta across frames. /// // Dans DollyZoomEffect, stocker le FOV base séparément private void TickDollyZoom() { float speedKmh = GetHorizontalSpeedKmh(); float speedT = Mathf.Clamp01( (speedKmh - _speedThreshold) / Mathf.Max(0.01f, _maxSpeedForEffect - _speedThreshold) ); float targetOffset = _maxFovOffset * speedT; if (_enablePulse && speedT > 0.01f) targetOffset += Mathf.Sin(Time.time * _pulseFrequency * Mathf.PI * 2f) * _pulseAmplitude * speedT; _currentFovOffset = Mathf.Lerp(_currentFovOffset, targetOffset, Time.deltaTime * _smoothSpeed); _playerCamera.fieldOfView += _currentFovOffset - _previousFovOffset; _previousFovOffset = _currentFovOffset; } // ───────────────────────────────────────────────────────────────────────── // Helpers // ───────────────────────────────────────────────────────────────────────── private float GetHorizontalSpeedKmh() { CharacterController charController = PlayerController.Instance?.CharController; if (charController == null) return 0f; Vector3 horizontalVelocity = charController.velocity; horizontalVelocity.y = 0f; return horizontalVelocity.magnitude * 3.6f; } // ───────────────────────────────────────────────────────────────────────── // Public API // ───────────────────────────────────────────────────────────────────────── public void SetEnabled(bool value) => _isEnabled = value; } }