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. /// public class CameraHeadBob : MonoBehaviour { private float _timer = 0f; private Vector3 _startLocalPos; private HeadBobMode _overrideMode = HeadBobMode.Normal; private bool _hasOverride = false; private Coroutine _resetCoroutine; void Start() { _startLocalPos = transform.localPosition; } void Update() { if (PlayerController.Instance == null || PlayerController.Instance.CharController == null) return; var settings = PlayerController.Instance.CameraEffects?.Settings; if (settings == null) return; HeadBobMode currentMode = _hasOverride ? _overrideMode : HeadBobMode.Normal; if (currentMode == HeadBobMode.Disabled) { ResetPosition(settings.resetSpeed); return; } if (currentMode == HeadBobMode.Idle || (PlayerInspectionHandler.Instance != null && PlayerInspectionHandler.Instance.IsInspecting)) { ApplyBob(settings.idleBobSpeed, settings.idleBobAmplitude); return; } bool isMoving = PlayerController.Instance.IsMoving; bool isRunning = PlayerController.Instance.IsRunning; bool isGrounded = PlayerController.Instance.CharController.isGrounded; if (isGrounded) { if (isMoving) { if (isRunning) ApplyBob(settings.runBobSpeed, settings.runBobAmplitude); else ApplyBob(settings.walkBobSpeed, settings.walkBobAmplitude); } else ApplyBob(settings.idleBobSpeed, settings.idleBobAmplitude); } else ResetPosition(settings.resetSpeed); } public void SetMode(HeadBobMode mode) { _overrideMode = mode; _hasOverride = true; } public void ClearOverride() { _hasOverride = false; if (_resetCoroutine != null) { StopCoroutine(_resetCoroutine); _resetCoroutine = null; } } public void LerpToZero(float duration) { if (_resetCoroutine != null) StopCoroutine(_resetCoroutine); _resetCoroutine = StartCoroutine(LerpToZeroCoroutine(duration)); } private System.Collections.IEnumerator LerpToZeroCoroutine(float duration) { Vector3 startPos = transform.localPosition; float elapsed = 0f; while (elapsed < duration) { elapsed += Time.deltaTime; float t = Mathf.Clamp01(elapsed / duration); transform.localPosition = Vector3.Lerp(startPos, Vector3.zero, t); yield return null; } transform.localPosition = Vector3.zero; _resetCoroutine = null; } 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(float resetSpeed) { transform.localPosition = Vector3.Lerp(transform.localPosition, _startLocalPos, Time.deltaTime * resetSpeed); if (Vector3.Distance(transform.localPosition, _startLocalPos) < 0.001f) _timer = 0f; } } }