Skip to content

Instantly share code, notes, and snippets.

@Bocom
Created April 1, 2014 15:04
Show Gist options
  • Save Bocom/9916005 to your computer and use it in GitHub Desktop.
Save Bocom/9916005 to your computer and use it in GitHub Desktop.
using System.Collections;
using UnityEngine;
// Shakes the camera if placed on the camera's GameObject or a parent of the camera's GameObject.
// (Putting it on a parent of the camera stops it potentially interfering with other camera scripts).
public class CameraShake : MonoBehaviour {
// How fast the shaking magnitude reduces - this much per second
const float shakeFalloffSpeed = 2.0f;
// How quickly the camera shakes around
const float shakeSpeed = 20.0f;
// If a shake is called when the camera is already shaking, its additional intensity will be
// reduced proportionately to how much the camera is shaking already. Eventually the addition will be almost zero;
// so a hundred things causing shake at once won't have insanely huge camera shake.
// Lower values = FASTER falloff.
const float addFalloff = 0.1f;
// Minimum multiplier
// Means we stop bothering to add shakes when there is already a lot of shaking
const float minMultiplierToAdd = 0.15f;
Transform tr; // Cached reference
float curMagnitude;
bool coroutineIsRunning = false;
public static bool Exists {
get { return instance != null; }
}
// Note that this isn't a self-creating singleton - instance will be null if this script isn't in the scene, so scripts need to do a null check.
// Can use Exists for that, or null check after GetInstance
static CameraShake instance;
public static CameraShake GetInstance {
get {
if (instance == null) {
Debug.LogWarning("A script tried to get the CameraShake script but there's none in the scene. Will return null.");
}
return instance;
}
}
// #### UNITY INTERNAL METHODS ####
void Awake() {
tr = transform;
instance = this;
}
// #### PUBLIC METHODS ####
public void ShakeCamera(float magnitude) {
magnitude *= 0.01f; // We divide by 100 here just so nicer values can be set for the input. Otherwise they end up very small
float multiplier = Mathf.Min(1.0f, Mathf.Pow(addFalloff, curMagnitude));
// Avoid going forever with ridiculously small values if lots of shakes are called at once
if (multiplier < minMultiplierToAdd) {
return;
}
// NOTE: The multiplier system avoids the shakes getting too big, but it has sort of an archilles' heel.
// It sort of normalises the shakes so e.g. several large shake calls will end up closer to several small shake calls than they should be.
// I can't think of a better way to handle it right now without a lot of extra data to remember the details of previous shakes.
// Adds magnitude instead of setting it, so multiple shake commands can add together
curMagnitude += magnitude * multiplier;
// If a shake is already being performed, we can just increase the magnitude (above) and it'll pick up the change and run for longer.
// Start the shake coroutine if there isn't any shaking already
if (!coroutineIsRunning) {
coroutineIsRunning = true;
StartCoroutine(Shake());
}
}
// #### PRIVATE METHODS ####
IEnumerator Shake() {
ShakeCamera();
yield return null; // Will continue next frame
while (curMagnitude > (shakeFalloffSpeed * Time.deltaTime)) {
ShakeCamera();
yield return null;
}
tr.position = Vector3.zero; // Finish the shake definitively
curMagnitude = 0;
coroutineIsRunning = false;
}
void ShakeCamera() {
float val = Time.time * shakeSpeed;
float xOffset = curMagnitude * Mathf.PerlinNoise(val, 0);
float yOffset = curMagnitude * Mathf.PerlinNoise(0, val);
float zOffset = curMagnitude * Mathf.PerlinNoise(val, val);
tr.position = new Vector3(xOffset, yOffset, zOffset);
curMagnitude -= (shakeFalloffSpeed * Time.deltaTime);
}
}
//Called like this:
if (CameraShake.Exists) CameraShake.GetInstance.ShakeCamera(w.CameraShakeAmount);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment