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);
}
}
}
}