Skip to content

Instantly share code, notes, and snippets.

@ftvs
Last active September 26, 2024 07:16
Show Gist options
  • Save ftvs/5822103 to your computer and use it in GitHub Desktop.
Save ftvs/5822103 to your computer and use it in GitHub Desktop.
Simple camera shake effect for Unity3d, written in C#. Attach to your camera GameObject. To shake the camera, set shakeDuration to the number of seconds it should shake for. It will start shaking if it is enabled.
using UnityEngine;
using System.Collections;
public class CameraShake : MonoBehaviour
{
// Transform of the camera to shake. Grabs the gameObject's transform
// if null.
public Transform camTransform;
// How long the object should shake for.
public float shakeDuration = 0f;
// Amplitude of the shake. A larger value shakes the camera harder.
public float shakeAmount = 0.7f;
public float decreaseFactor = 1.0f;
Vector3 originalPos;
void Awake()
{
if (camTransform == null)
{
camTransform = GetComponent(typeof(Transform)) as Transform;
}
}
void OnEnable()
{
originalPos = camTransform.localPosition;
}
void Update()
{
if (shakeDuration > 0)
{
camTransform.localPosition = originalPos + Random.insideUnitSphere * shakeAmount;
shakeDuration -= Time.deltaTime * decreaseFactor;
}
else
{
shakeDuration = 0f;
camTransform.localPosition = originalPos;
}
}
}
@AnKlagges92
Copy link

AnKlagges92 commented Sep 9, 2023

Here's my script to shake, feel free to use and modify.

using UnityEngine;
#if ODIN_INSPECTOR
using Sirenix.OdinInspector;
#endif

namespace AKGaming
{
    public class ShakeComponent : MonoBehaviour
    {
        [SerializeField] private Transform target_;
        [SerializeField] private float defaultDuration_ = 0.5f;
        [SerializeField] private float defaultAmplitude_ = 0.75f;
        [SerializeField] private float defaultFinalAmplitude_ = 0.25f;
        [SerializeField] private AnimationCurve defaultAmplitudeCurve_ = AnimationCurve.Linear(0, 0, 1, 1);

        private float duration_;
        private float remainingTime_;
        private float amplitude_;
        private float finalAmplitude_;
        private AnimationCurve amplitudeCurve_;
        private Vector3 initialPosition_;

        private void Awake()
        {
            if (target_ == null)
            {
                target_ = GetComponent<Transform>();
            }
            initialPosition_ = target_.localPosition;
        }

        public void Shake(float duration = -1, float amplitude = -1, float finalAmplitude = -1, AnimationCurve amplitudeCurve = null)
        {
            duration_ = duration > 0 ? duration : defaultDuration_;
            amplitude_ = amplitude >= 0 ? amplitude : defaultAmplitude_;
            finalAmplitude_ = finalAmplitude >= 0 ? finalAmplitude : defaultFinalAmplitude_;
            amplitudeCurve_ = amplitudeCurve != null ? amplitudeCurve : defaultAmplitudeCurve_;

            remainingTime_ = duration_;
            enabled = true;
        }

        private void Update()
        {
            if (remainingTime_ > 0)
            {
                float curveValue = amplitudeCurve_.Evaluate(1 - (remainingTime_ / duration_));
                float amplitude = (1 - curveValue) * amplitude_ + curveValue * finalAmplitude_;
                transform.localPosition = initialPosition_ + Random.insideUnitSphere * amplitude;
                remainingTime_ -= Time.deltaTime;
            }
            else
            {
                remainingTime_ = 0f;
                transform.localPosition = initialPosition_;
                enabled = false;
            }
        }

#if UNITY_EDITOR
#if ODIN_INSPECTOR
        [Button("Test Shake")]
#endif
        [ContextMenu("Test Shake")]
        private void EDITOR_TestShake()
        {
            Shake();
        }
#endif
    }
}

Here's the amplitude curve i'm using right now.
Please notice that the curve value is not the actual amplitude value so I recommend that it always goes from 0 to 1.

@PazoGames
Copy link

Thanks! It worked well. The only thing I added to the script was a line that turns CinemachineBrain false because camera shaker didn't work with cinemachine.

@Ar-Ess
Copy link

Ar-Ess commented Apr 17, 2024

This is my modification, I made it completely generic. Just copy paste on any object and you're good to go.

How it works:

  • Duration: the duration of the shake in seconds
  • Amplitude: the amount of Unity units that the shake uses to move
  • Soft Level: The higher it is, the slower will run the shake. Internally, the soft level int acts as "how many frames do I skip for the next shake"
  • Decrease: If false, the shake will be constant. If true, the shake will linearly shift its amplitude.
  • Animation: Same as decrease, but here you can choose which amplitude animation you want

Flaws:

  • The soften parameter does its job, but the shake is not really smooth... I tried implementing the "Vector3.Lerp" option, but it does not work really well... If someone can come up with a solution to smooth the movement, so then we can acquire a kind of "frequency" parameter.
public void Shake(float duration, float amplitude, int softLevel = 0, bool decrease = false)
{
    AnimationCurve animation = decrease ? AnimationCurve.Linear(0, 1, 1, 0) : AnimationCurve.Constant(0, 1, 1);
    StartCoroutine(Shake_Internal(duration, amplitude, softLevel, animation));
}

public void Shake(float duration, float amplitude, int softLevel = 0, AnimationCurve animation = null)
{
    if (animation == null) animation = AnimationCurve.Linear(0, 1, 1, 0);
    StartCoroutine(Shake_Internal(duration, amplitude, softLevel, animation));
}

private IEnumerator Shake_Internal(float duration, float amplitude, int softLevel, AnimationCurve animation)
{
    Vector3 initialPosition = transform.position;
    float amp = amplitude;
    if (softLevel < 0) softLevel = 0;
    int softCount = 0;

    for (float i = 0; i < duration; i += Time.deltaTime)
    {
        if (softLevel != 0 && softCount < softLevel)
            softCount++;
        else
        {
            transform.position = initialPosition + Random.insideUnitSphere * amp;
            amp = amplitude * animation.Evaluate(i);
            softCount = 0;
        }
        yield return null;
    }

    transform.position = initialPosition;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment