using UnityEngine; using Unheimlich.Interactions.Core; using Unheimlich.Data.CameraSettings; namespace Unheimlich.Gameplay.Player { /// /// Simulates camera head bobbing motion during player movement and idle states. /// Applies subtle oscillation based on player grounded and movement state. /// public class CameraHeadBob : MonoBehaviour { [Header("Bob Settings - Idle")] [SerializeField] private float _idleBobSpeed = 1f; [SerializeField] private float _idleBobAmplitude = 0.005f; [Header("Bob Settings - Walk")] [SerializeField] private float _walkBobSpeed = 12f; [SerializeField] private float _walkBobAmplitude = 0.02f; [SerializeField] private float _resetSpeed = 5f; private PlayerMovement _playerMovement; private float _timer = 0f; private Vector3 _startLocalPos; private HeadBobMode _overrideMode = HeadBobMode.Normal; private bool _hasOverride = false; void Start() { _playerMovement = PlayerMovement.Instance; _startLocalPos = transform.localPosition; } void Update() { if (_playerMovement == null || _playerMovement.CharacterController == null) return; HeadBobMode currentMode = _hasOverride ? _overrideMode : HeadBobMode.Normal; if (currentMode == HeadBobMode.Disabled) { ResetPosition(); return; } if (currentMode == HeadBobMode.Idle || (PlayerInspectionHandler.Instance != null && PlayerInspectionHandler.Instance.IsInspecting)) { ApplyBob(_idleBobSpeed, _idleBobAmplitude); return; } bool isMoving = _playerMovement.IsMoving; bool isGrounded = _playerMovement.CharacterController.isGrounded; if (isGrounded) { if (isMoving) ApplyBob(_walkBobSpeed, _walkBobAmplitude); else ApplyBob(_idleBobSpeed, _idleBobAmplitude); } else ResetPosition(); } /// /// Sets the head bob mode override. /// public void SetMode(HeadBobMode mode) { _overrideMode = mode; _hasOverride = true; } /// /// Clears the override and returns to normal behavior. /// public void ClearOverride() { _hasOverride = false; } private void ApplyBob(float speed, float amplitude) { _timer += Time.deltaTime * speed; float x = Mathf.Sin(_timer * 0.5f) * amplitude; float y = Mathf.Cos(_timer) * amplitude; Vector3 newPos = _startLocalPos; newPos.x += x; newPos.y += y; transform.localPosition = newPos; } private void ResetPosition() { transform.localPosition = Vector3.Lerp(transform.localPosition, _startLocalPos, Time.deltaTime * _resetSpeed); if (Vector3.Distance(transform.localPosition, _startLocalPos) < 0.001f) _timer = 0f; } } }