using System.Collections; using UnityEngine; namespace KhawajjaGames.Utilities { /// /// Production-grade Camera Shake system for Unity. /// Uses non-linear trauma with Perlin Noise for smooth, visceral impact feedback. /// Created by Khawajja Games Studio (https://khawajjagames.com) /// public class CameraShake : MonoBehaviour { public static CameraShake Instance { get; private set; } [Header("Trauma Settings")] [Tooltip("Trauma reduction rate per second")] [Range(0.1f, 5f)] public float traumaDecay = 1.2f; [Header("Translation Amplitude")] public float maxTranslationX = 0.6f; public float maxTranslationY = 0.6f; public float maxTranslationZ = 0.2f; [Header("Rotational Amplitude (Degrees)")] public float maxPitch = 4.0f; public float maxYaw = 4.0f; public float maxRoll = 6.0f; [Header("Frequency")] public float frequency = 25f; private float _trauma = 0f; private Vector3 _initialLocalPosition; private Quaternion _initialLocalRotation; private float _seed; private void Awake() { if (Instance == null) { Instance = this; } else { Destroy(gameObject); return; } _initialLocalPosition = transform.localPosition; _initialLocalRotation = transform.localRotation; _seed = Random.value * 100f; } private void Update() { if (_trauma > 0f) { // Non-linear trauma curve (squared gives Punchy & Natural feel) float shake = _trauma * _trauma; float time = Time.time * frequency; // Perlin noise offsets for smooth translation float offsetX = (Mathf.PerlinNoise(_seed, time) * 2f - 1f) * maxTranslationX * shake; float offsetY = (Mathf.PerlinNoise(_seed + 1, time) * 2f - 1f) * maxTranslationY * shake; float offsetZ = (Mathf.PerlinNoise(_seed + 2, time) * 2f - 1f) * maxTranslationZ * shake; transform.localPosition = _initialLocalPosition + new Vector3(offsetX, offsetY, offsetZ); // Rotational wobble float pitch = (Mathf.PerlinNoise(_seed + 3, time) * 2f - 1f) * maxPitch * shake; float yaw = (Mathf.PerlinNoise(_seed + 4, time) * 2f - 1f) * maxYaw * shake; float roll = (Mathf.PerlinNoise(_seed + 5, time) * 2f - 1f) * maxRoll * shake; transform.localRotation = _initialLocalRotation * Quaternion.Euler(pitch, yaw, roll); // Decay trauma over time _trauma = Mathf.Clamp01(_trauma - traumaDecay * Time.deltaTime); } else { transform.localPosition = _initialLocalPosition; transform.localRotation = _initialLocalRotation; } } /// /// Add stress/trauma to the camera (0.0 to 1.0). /// Values accumulate and are capped at 1.0. /// /// Amount of trauma to add (e.g. 0.3 for light gunshot, 0.8 for bomb explosion) public void AddTrauma(float amount) { _trauma = Mathf.Clamp01(_trauma + amount); } /// /// Trigger a camera shake with a custom stress amount. /// public static void Shake(float amount) { if (Instance != null) { Instance.AddTrauma(amount); } } } }