A good hit, explosion or landing feels flat without a little camera movement. The trick is making Unity screen shake that’s punchy but not nauseating, and that doesn’t fight your camera system. This guide covers two approaches for Unity 6: Cinemachine 3’s Impulse system, which plays nicely with follow cameras, and a lightweight trauma-based script for projects without Cinemachine. Then we add hit stop and the settings that keep shake comfortable for every player.
Why Shaking the Camera Directly Goes Wrong
The first instinct is to nudge Camera.main.transform.position by a random offset for a few frames. That works until something else also moves the camera. If a follow script or Cinemachine sets the position every frame, your offset is overwritten instantly (no shake at all) or it accumulates and the camera drifts away from the player.
Two rules avoid this:
- Shake an offset, not the base position. Either put the camera inside a parent object that follows the player and shake the child’s
localPosition, or let Cinemachine add the shake after it computes its own position. - Use smooth noise, not pure random.
Random.insideUnitSphereevery frame produces jittery static. Perlin noise gives a rolling motion that reads as impact rather than glitch.
Cinemachine Impulse Setup
If you already use Cinemachine 3 (the default camera package for Unity 6), Impulse is the cleanest option. It separates sources (things that cause shake) from listeners (cameras that react), so a grenade can shake every camera near it with distance falloff. If you’re new to Cinemachine 3, start with our Cinemachine 3 third-person camera guide.
1. Add a listener to the camera
Select your CinemachineCamera. At the bottom of its Inspector, use Add Extension and choose CinemachineImpulseListener. Key settings:
- Gain: a multiplier for this camera. Set it to 0 on cameras that should never shake, such as a map view.
- Use 2D Distance: tick for 2D games so the Z distance is ignored.
- Channel Mask: lets cameras ignore certain kinds of impulse.
- Reaction Settings: optionally adds a secondary noise profile that plays after each impulse, for a more organic settle.
2. Add a source
On the object that causes shake (a weapon, an explosion prefab, the player for landings), add CinemachineImpulseSource. In its Impulse Definition:
- Impulse Shape: pick a preset. Recoil is a quick kick for gunfire, Bump for landings, Explosion for big hits, Rumble for sustained shaking. You can also draw a Custom curve.
- Impulse Duration: 0.15 to 0.3 seconds for most hits.
- Default Velocity: the direction and strength of the shake. A small Y value like (0, -0.25, 0) makes a downward thump; a random-feeling explosion uses a mix of axes.
- Impulse Type: Uniform shakes all listeners equally, Dissipating fades with distance, Propagating also delays it as if a wave travelled outward. Use Dissipating for explosions in 3D worlds.
Press Play and click Invoke in the source’s Inspector to preview the shake without writing any code.
Triggering Impulses From Code
Cinemachine 3 lives in the Unity.Cinemachine namespace. Cache the source and call one of its generate methods:
using UnityEngine;
using Unity.Cinemachine;
[RequireComponent(typeof(CinemachineImpulseSource))]
public class ShakeOnHit : MonoBehaviour
{
CinemachineImpulseSource impulse;
void Awake() => impulse = GetComponent<CinemachineImpulseSource>();
public void SmallHit() => impulse.GenerateImpulse(); // Default Velocity
public void BigHit() => impulse.GenerateImpulseWithForce(2.5f); // scaled
public void Knockback(Vector3 direction)
{
// Shake in the direction of the hit
impulse.GenerateImpulseWithVelocity(direction.normalized * 0.4f);
}
}
GenerateImpulseWithForce multiplies the Default Velocity, so one source can serve light and heavy attacks by scaling the force with damage. Because impulses are additive, several explosions at once stack naturally. A good pattern is to fire impulses from events (for example a Damaged event on your Health component) so gameplay code doesn’t need to know about cameras at all; our C# events guide shows how.
A Trauma-Based Shake Script
Without Cinemachine, the “trauma” approach popularised by game feel talks works beautifully. Each hit adds trauma (0 to 1), trauma decays over time, and the shake amount is trauma squared, so small hits are subtle and big ones are dramatic.
Set up the hierarchy as CameraRig (follows the player) with the Main Camera as a child at local position zero. Put this script on the child camera:
using UnityEngine;
public class TraumaShake : MonoBehaviour
{
public static TraumaShake Instance { get; private set; }
[SerializeField] float maxOffset = 0.5f; // world units
[SerializeField] float maxAngle = 4f; // degrees (roll)
[SerializeField] float frequency = 25f; // noise speed
[SerializeField] float decayPerSecond = 1.5f;
float trauma;
float seed;
void Awake()
{
Instance = this;
seed = Random.value * 100f;
}
public void AddTrauma(float amount) => trauma = Mathf.Clamp01(trauma + amount);
void LateUpdate()
{
if (trauma <= 0f)
{
transform.localPosition = Vector3.zero;
transform.localRotation = Quaternion.identity;
return;
}
float shake = trauma * trauma;
float t = Time.unscaledTime * frequency;
float x = (Mathf.PerlinNoise(seed, t) * 2f - 1f) * maxOffset * shake;
float y = (Mathf.PerlinNoise(seed + 1f, t) * 2f - 1f) * maxOffset * shake;
float roll = (Mathf.PerlinNoise(seed + 2f, t) * 2f - 1f) * maxAngle * shake;
transform.localPosition = new Vector3(x, y, 0f);
transform.localRotation = Quaternion.Euler(0f, 0f, roll);
trauma = Mathf.Max(0f, trauma - decayPerSecond * Time.unscaledDeltaTime);
}
}
Call TraumaShake.Instance.AddTrauma(0.3f) for a hit and 0.8f for an explosion. A few details make it work:
- It runs in
LateUpdate, after the rig has followed the player. - It uses unscaled time, so shake continues during hit stop or slow motion.
- It only touches local position and rotation, so the rig’s follow logic is never disturbed.
- Roll rotation adds a lot of energy for a small offset. For 2D games, roll plus small X/Y offsets looks great. For first-person games, use rotation only (pitch and yaw), since moving the position can clip the camera into walls.
Hit Stop
Hit stop (or hit freeze) pauses the game for a few frames on impact. Combined with shake, it’s what makes melee hits feel heavy. The simplest version sets the time scale to zero briefly:
using System.Collections;
using UnityEngine;
public class HitStop : MonoBehaviour
{
public static HitStop Instance { get; private set; }
bool running;
void Awake() => Instance = this;
public void Freeze(float seconds)
{
if (!running) StartCoroutine(FreezeRoutine(seconds));
}
IEnumerator FreezeRoutine(float seconds)
{
running = true;
float previous = Time.timeScale;
Time.timeScale = 0f;
yield return new WaitForSecondsRealtime(seconds);
Time.timeScale = previous;
running = false;
}
}
Use WaitForSecondsRealtime, because ordinary WaitForSeconds never finishes while time scale is zero. Typical durations are 0.03 to 0.08 seconds for normal hits and up to 0.15 seconds for a finishing blow. Longer than that and players read it as lag. If your game already has a pause menu that sets time scale, make sure hit stop doesn’t restore the wrong value when both overlap.
Cinemachine cameras also respect time scale by default. If you want impulses to keep moving during hit stop, set the Cinemachine Brain’s Ignore Time Scale option.
✨ Want more juice with less code?
Feedback and tweening tools bundle screen shake, hit stop, flashes and squash and stretch into drag-and-drop components. These are the Asset Store tools we’d buy first.
Keeping Shake Comfortable
Screen shake is one of the most common causes of motion sickness complaints. Good habits:
- Add a settings slider. Multiply all shake by a
shakeScalevalue from 0 to 1 that the player controls, and respect 0 completely. With Cinemachine, set the listener’s Gain from the slider. - Keep it short. Most shakes should finish in under 0.4 seconds. Long rumbles are for rare, big moments.
- Shake less for the player’s own actions. Firing a pistol 10 times a second with heavy shake becomes exhausting. Save strong shake for taking damage and explosions.
- Prefer rotation in first person. Small rotational kicks feel like recoil; large position offsets feel like the world is broken.
- Cap the total. Clamping trauma at 1 (or limiting impulse force) stops five simultaneous explosions turning the screen into soup.
Troubleshooting
Cinemachine impulse does nothing
The CinemachineCamera has no Impulse Listener extension, its Gain is 0, or the source and listener channel masks don’t overlap. Also check Default Velocity isn’t zero.
Shake works in the Inspector but not from code
You’re calling a different source than you think, or the impulse type is Dissipating and the source is far from the camera. Increase the Dissipation Distance or switch to Uniform to test.
The camera drifts away after shaking
Your shake script adds offsets to the position each frame instead of setting them. Shake a child object’s local position and reset it to zero when trauma is gone.
Shake stops during hit stop
It uses scaled time. Switch to Time.unscaledTime and Time.unscaledDeltaTime, or tick Ignore Time Scale on the Cinemachine Brain.
Game stays frozen after hit stop
The coroutine was stopped (the object was disabled or destroyed) before restoring time scale. Keep HitStop on a persistent object, and reset Time.timeScale when loading scenes.
FAQ
Should I use Cinemachine Impulse or a custom script?
If you already use Cinemachine, use Impulse because it layers cleanly on top of camera movement and supports distance falloff. Otherwise, a trauma script on a child camera is simple and effective.
Does Cinemachine Impulse work in 2D?
Yes. Tick Use 2D Distance on the listener and use mostly X/Y velocity in the source.
Why use Perlin noise instead of Random?
Perlin noise changes smoothly between frames, giving a rolling shake. Pure random values look like jittery static.
How long should hit stop last?
Around 0.03 to 0.08 seconds for regular hits, up to about 0.15 for big finishers.
Can screen shake cause motion sickness?
Yes, for some players. Always offer a slider that can turn it fully off.
Reader questions
Asked by readers of this article and answered by AI. Behavior can differ between Unity versions, so test in yours.




Leave a Reply