Camera Shake
Perlin noise camera shake with magnitude, frequency, and duration. Trigger from explosions, impacts, or any event.
How to Use
Attach CameraShake to your main Camera
Call CameraShake.Instance.Shake() from any script
Custom intensity: CameraShake.Instance.Shake(0.5f, 0.3f)
Subtle hit: Shake(0.15f, 0.05f) | Explosion: Shake(0.6f, 0.5f)
Uses Perlin noise for smooth, natural-feeling shake
Auto-decays over duration, multiple shakes stack naturally
Features
- Perlin noise-based shake for smooth, organic camera movement
- Configurable duration, magnitude, and frequency parameters
- Automatic linear decay so shake fades out naturally
- Stronger shakes override weaker ones instead of stacking chaotically
- Uses unscaledDeltaTime so shake works during slow-motion effects
- Singleton access — trigger from any script with one line of code
When to Use This
Great for FPS games (weapon recoil, explosions), platformers (landing impacts, boss stomps), and action games in general. Use this whenever you want to add screen feedback for impactful events — hits, explosions, environmental shakes, or dramatic moments. Works in both 2D and 3D games.
Common Mistakes
If using with a camera follow script, make sure CameraShake modifies localPosition, not position — both scripts fighting over world position causes jitter. Placing this on a root-level camera works fine, but on a child camera (like in an FPS setup) the localPosition offset is relative to the parent. Setting magnitude too high (above 1.0) makes the shake look broken rather than dramatic — start with values between 0.05 and 0.5.
Source Code
using UnityEngine;
/// <summary>
/// Perlin noise-based camera shake. Attach to your camera.
/// Call Shake() to trigger from any script.
/// </summary>
public class CameraShake : MonoBehaviour
{
public static CameraShake Instance { get; private set; }
[Header("Default Settings")]
[SerializeField] private float defaultDuration = 0.3f;
[SerializeField] private float defaultMagnitude = 0.15f;
[SerializeField] private float frequency = 25f;
private float shakeTimer;
private float shakeMagnitude;
private float shakeDuration;
private Vector3 originalLocalPosition;
private float seed;
private void Awake()
{
if (Instance == null)
Instance = this;
else
{
Destroy(this);
return;
}
originalLocalPosition = transform.localPosition;
seed = Random.value * 1000f;
}
private void Update()
{
if (shakeTimer <= 0f)
{
transform.localPosition = originalLocalPosition;
return;
}
shakeTimer -= Time.unscaledDeltaTime;
float decay = shakeTimer / shakeDuration;
float offsetX = (Mathf.PerlinNoise(seed, Time.unscaledTime * frequency) * 2f - 1f) * shakeMagnitude * decay;
float offsetY = (Mathf.PerlinNoise(seed + 1f, Time.unscaledTime * frequency) * 2f - 1f) * shakeMagnitude * decay;
transform.localPosition = originalLocalPosition + new Vector3(offsetX, offsetY, 0f);
}
/// <summary>
/// Trigger a camera shake with default settings.
/// </summary>
public void Shake()
{
Shake(defaultDuration, defaultMagnitude);
}
/// <summary>
/// Trigger a camera shake with custom duration and magnitude.
/// </summary>
public void Shake(float duration, float magnitude)
{
// Allow stacking: use the stronger shake
if (shakeTimer > 0f && magnitude <= shakeMagnitude)
return;
shakeDuration = duration;
shakeMagnitude = magnitude;
shakeTimer = duration;
seed = Random.value * 1000f;
}
}