To anyone still trying to find an answer. Or someone who might want to improve my code here it goes.
This works if you put your camera GameObject as a child of another Transform (this parent Transform holds the true position of the camera while we shake the camera by changing the childs transform position rotation and fov).
First let’s define a class for our shake….
public class CameraShake
{
public struct ElementShake
{
public float amplitude,
frequency;
public ElementShake(float amplitude, float frequency)
{
this.amplitude = amplitude;
this.frequency = frequency;
}
}
public float duration,
blendInTime,
blendOutTime;
public ElementShake[] rotationShake = new ElementShake[3];
public ElementShake[] positionShake = new ElementShake[3];
public ElementShake fieldOfViewShake;
public CameraShake()
: this(1f, 0.1f, 0.2f)
{
}
public CameraShake(float duration, float blendInTime, float blendOutTime)
{
this.duration = duration;
this.blendInTime = blendInTime;
this.blendOutTime = blendOutTime;
for (int i = 0; i < rotationShake.Length; i++)
rotationShake *= new ElementShake(0f, 0f);*
for (int i = 0; i < positionShake.Length; i++)
rotationShake = new ElementShake(0f, 0f);
fieldOfViewShake = new ElementShake(0f, 0f);
}
}
Now lets define our player controller :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayaController : MonoBehaviour
{
internal struct CameraShakeSettings
{
public Vector3 pos,
initialPosition;
public Vector3 rot,
initialRotation;
public float fov,
initialFieldOfView;
public Coroutine shakeCoroutine;
}
public Camera mainCamera;
static PlayaController controller;
private CameraShakeSettings cameraShakeSettings = new CameraShakeSettings();
private void Awake()
{
controller = this;
}
private void Start()
{
cameraShakeSettings.initialPosition = mainCamera.transform.localPosition;
cameraShakeSettings.initialRotation = mainCamera.transform.localRotation.eulerAngles;
cameraShakeSettings.initialFieldOfView = mainCamera.fieldOfView;
}
public static void PlayCameraShake(CameraShake cameraShake, float scale = 1f)
{
if (controller == null)
return;
if (controller.cameraShakeSettings.shakeCoroutine != null)
controller.StopCoroutine(controller.cameraShakeSettings.shakeCoroutine);
controller.cameraShakeSettings.shakeCoroutine = controller.StartCoroutine(controller.ShakeCamera(cameraShake, scale));
}
private IEnumerator ShakeCamera(CameraShake cameraShake, float scale)
{
float blendScale = 0f;
float blendInTimer = cameraShake.duration * Mathf.Min(cameraShake.blendInTime, 1f);
float blendOutTimer = cameraShake.duration * Mathf.Min(cameraShake.blendOutTime, 1f);
float timer = 0f;
Camera cam = mainCamera;
Transform t = cam.transform;
while (timer <= cameraShake.duration)
{
float blendInScale = Mathf.Clamp01(timer / blendInTimer);
float blendOutScale = Mathf.Clamp01((cameraShake.duration - timer) / blendOutTimer);
blendScale = scale * blendInScale * blendOutScale;
float blendTime = timer / cameraShake.duration;
cameraShakeSettings.rot.x = (Mathf.Cos(blendTime * cameraShake.rotationShake[0].frequency * Mathf.Rad2Deg) * cameraShake.rotationShake[0].amplitude) * blendScale;
cameraShakeSettings.rot.y = (Mathf.Cos(blendTime * cameraShake.rotationShake[1].frequency * Mathf.Rad2Deg) * cameraShake.rotationShake[1].amplitude) * blendScale;
cameraShakeSettings.rot.z = (Mathf.Cos(blendTime * cameraShake.rotationShake[2].frequency * Mathf.Rad2Deg) * cameraShake.rotationShake[2].amplitude) * blendScale;
t.localRotation = Quaternion.Euler(cameraShakeSettings.initialRotation.x + cameraShakeSettings.rot.x,
cameraShakeSettings.initialRotation.y + cameraShakeSettings.rot.y,
cameraShakeSettings.initialRotation.z + cameraShakeSettings.rot.z);
cameraShakeSettings.pos.x = (Mathf.Cos(blendTime * cameraShake.positionShake[0].frequency * Mathf.Rad2Deg) * cameraShake.positionShake[0].amplitude);
cameraShakeSettings.pos.y = (Mathf.Cos(blendTime * cameraShake.positionShake[1].frequency * Mathf.Rad2Deg) * cameraShake.positionShake[1].amplitude);
cameraShakeSettings.pos.z = (Mathf.Cos(blendTime * cameraShake.positionShake[2].frequency * Mathf.Rad2Deg) * cameraShake.positionShake[2].amplitude);
t.localPosition = cameraShakeSettings.initialPosition + cameraShakeSettings.pos * blendScale;
cameraShakeSettings.fov = (Mathf.Cos(blendTime * cameraShake.fieldOfViewShake.frequency * Mathf.Deg2Rad) * cameraShake.fieldOfViewShake.amplitude) * blendScale;
cam.fieldOfView = cameraShakeSettings.initialFieldOfView + cameraShakeSettings.fov;
timer += Time.deltaTime;
yield return null;
}
t.localRotation = Quaternion.Euler(cameraShakeSettings.initialRotation);
t.localPosition = cameraShakeSettings.initialPosition;
cam.fieldOfView = cameraShakeSettings.initialFieldOfView;
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.S))
{
CameraShake shake = new CameraShake(1f, 0.5f, 0.5f);
//Rotation shake On X axis
shake.rotationShake[0].amplitude = 0.1f;
shake.rotationShake[0].frequency = 10000;
//Rotation shake ON Y axis
shake.rotationShake[1].amplitude = 0.1f;
shake.rotationShake[1].frequency = 10000;
//Rotation shake On Z axis
shake.rotationShake[2].amplitude = 0.1f;
shake.rotationShake[2].frequency = 10000;
//Position shake On X axis
shake.positionShake[0].amplitude = 0.1f;
shake.positionShake[0].frequency = 10000;
//Position shake On Y axis
shake.positionShake[1].amplitude = 0.1f;
shake.positionShake[1].frequency = 10000;
//Position shake On Z axis
shake.positionShake[2].amplitude = 0.1f;
shake.positionShake[2].frequency = 10000;
//Field of view shake
shake.fieldOfViewShake.amplitude = 1f;
shake.fieldOfViewShake.frequency = 100000;
PlayCameraShake(shake, 0.4f);
}
}
}
You should now be able to Press S and see the camera shake.
With this code you can just play the shake from any script by calling the player controller method like this :
PlayaController.PlayCameraShake(cameraShake);
If anybody finds this useful just comment or if you find a bug let me know I’ll correct it.
if you can find me a solution to play 2 camera shakes at the same time let me know.
Cheers everybody. Hope this helps.