using UnityEngine; [DisallowMultipleComponent] public sealed class PlayerCameraShakeController : MonoBehaviour, IPlayerSystem { private Transform cameraTransform; private Vector3 baseLocalPosition; private float amplitude; private float frequency; private float seed; private float endsAt; private float startedAt; private float duration; public bool IsShaking => cameraTransform != null && Time.time < endsAt; public void Initialize(PlayerFacade facade) { FirstPersonCameraController cameraController = GetComponent(); cameraTransform = cameraController != null && cameraController.PlayerCamera != null ? cameraController.PlayerCamera.transform : GetComponentInChildren(true)?.transform; if (cameraTransform != null) baseLocalPosition = cameraTransform.localPosition; } public void PlayShake( float newAmplitude, float newDuration, float newFrequency, float newSeed = 0f) { if (cameraTransform == null || newAmplitude <= 0f || newDuration <= 0f) return; float remainingStrength = IsShaking ? amplitude * Mathf.Clamp01((endsAt - Time.time) / Mathf.Max(0.01f, duration)) : 0f; amplitude = Mathf.Max(remainingStrength, newAmplitude); duration = newDuration; frequency = Mathf.Max(0.1f, newFrequency); seed = newSeed; startedAt = Time.time; endsAt = Time.time + newDuration; } private void LateUpdate() { if (cameraTransform == null) return; if (!IsShaking) { cameraTransform.localPosition = baseLocalPosition; return; } float normalizedTime = Mathf.Clamp01((Time.time - startedAt) / Mathf.Max(0.01f, duration)); float strength = amplitude * (1f - normalizedTime) * (1f - normalizedTime); float sample = (Time.time - startedAt) * frequency; Vector3 noise = new( Mathf.PerlinNoise(seed, sample) * 2f - 1f, Mathf.PerlinNoise(seed + 17.31f, sample) * 2f - 1f, Mathf.PerlinNoise(seed + 39.77f, sample) * 2f - 1f); cameraTransform.localPosition = baseLocalPosition + noise * strength; } private void OnDisable() { if (cameraTransform != null) cameraTransform.localPosition = baseLocalPosition; } }