Part of these game systems:
beginner Utilities

Camera Shake

Perlin noise camera shake with magnitude, frequency, and duration. Trigger from explosions, impacts, or any event.

Unity 2022.3+ · 1.8 KB · CameraShake.cs

How to Use

1

Attach CameraShake to your main Camera

2

Call CameraShake.Instance.Shake() from any script

3

Custom intensity: CameraShake.Instance.Shake(0.5f, 0.3f)

4

Subtle hit: Shake(0.15f, 0.05f) | Explosion: Shake(0.6f, 0.5f)

5

Uses Perlin noise for smooth, natural-feeling shake

6

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

CameraShake.cs
C#
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;
    }
}