Unity Camera Shake

For camera shake I start with a variable (which I’ll call trauma), which we decrement linearly over time, and accepts floating point values between 1 and 0. Our camera shake will be proportional to trauma squared or cubed.

I always try to avoid rooting values for performance reasons and this allows us to easily decrement the amount of camera shake of time, without having to root anything and allows a visible distinction between low, medium and high levels of camera shake.

To implement this we multiply our shake (trauma^2/^3) by our maxOffset and a randomly generated number. We get something like this:

offsetX = maxOffset * trauma^2 * SomeRandomNumber
offsetY = maxOffset * trauma^2 * AnotherRandomNumber
angle = maxAngle * trauma^2 * AnotherRandomNumber

There are two problems with this;

  1. It is frame rate dependent and not time based. This means that system performance will affect the visible camera shake, and this shake is not slow motion compatible and won’t work with tools like Chronos.
  2. Jerky camera movement, the camera sometimes appears to teleport between positions, rather than move between them over several frames.

This can be solved with Perlin Noise. With Perlin Noise, we can create camera shake that is time based and smoothed.

offsetX = maxOffset * trauma^2 * PerlinNoise(seed1,time)
offsetY = maxOffset * trauma^2 * PerlinNoise(seed2,time)
angle = maxAngle * trauma^2 * PerlinNoise(seed3,time)

We use different seeds for the each so that each motion is independent. To control how quickly the camera moves, replace your time with your time multipled by a speed constant. It should be noted that here you don’t want to use your deltaTime, but rather the current time/cumulative time.

You should also take care to preserve the original x, y values and the angle value. When you calculate your offset you want to add that to your base location/rotation, otherwise you will have your camera going everywhere. When moving and adjusting your camera in your script, you should alter these base preserved values

Translational vs Rotational

In 2 dimensions, pure translational camera shake looks alright, and pure rotational shake looks a bit off. By combining these in certain amounts we can create a pretty nice camera shake effect.

3 dimensions however, is different. In 3D, translational camera shake can have all sorts of bad effects, it can cause our camera to clip through walls and objects, make it hard to see and focus on objects, and cause motion sickness, especially in first person 3D games. Rotational shake however is usually very effective in 3D, and as it doesn’t change the position of the camera, you don’t have to worry about the camera clipping through walls.

A C# Unity Implementation/Example

float trauma = 0f;
Vector3 preserveOffset;
Vector3 preserveAngle;

public float maxAngle = 10f;
public float maxOffset = 1f;

public float shakeTranslationSpeed = 1f;
public float shakeRotationSpeed = 1f;

public float traumaLossRate = 0.01f;
public bool reduceTrauma = true;

float seed1;
float seed2;
float seed3;

public void addTrauma (float increment) {
	trauma = Mathf.Clamp(trauma + increment, 0, 1);
}

void Start () {
	preserveOffset = transform.localPosition;
	preserveAngle = tranform.localEulerAngles.z;
	
	//Seeds should be offset from each other Random.Range,
	//and non-overlapping ranges there is no way for two
	//seeds to be similar
	seed1 = Random.Range(300f,400f);
	seed2 = Random.Range(150f, 250f);
	seed3 = Random.Range(0f,100f);
}

void Update () {
	if (trauma > 0) {
		float shake = trauma^2;
		//translational camshake
		float offsetX = maxOffset * shake * Mathf.PerlinNoise(seed1, shakeTranslationSpeed*Time.time);
		float offsetY = maxOffset * shake * Mathf.PerlinNoise(seed2, shakeTranslationSpeed*Time.time);
		transform.localPosition = preserveOffset + new Vector3(offsetX,offsetY,0);
		//rotational camshake
		float angle = maxAngle * shake * Mathf.PerlinNoise(seed3, shakeRotationSpeed*Time.time);
		transform.localEulerAngles.z = preserveAngle + angle;
		
		//Allows for the option to have constant
		//non-decreasing cam shake without the need to 
		//continuously reapply trauma
		if (reduceTrauma) trauma = Mathf.Clamp(trauma-traumaLossRate*Time.deltaTime,0,1);
    }
}

Links

Unity Perlin Noise Documentation

Leave a Reply

Your email address will not be published. Required fields are marked *