unity3D实现摄像机抖动特效

本文实例为大家分享了unity3D实现摄像机抖动的具体代码,供大家参考,具体内容如下

摄像机抖动特效 在需要的地方调用CameraShake.Shake()方法就可以

public class CameraShake : MonoBehaviour

{

///

/// The cameras to shake.

///

public List cameras = new List();

///

/// The maximum number of shakes to perform.

///

public int numberOfShakes = 2;

///

/// The amount to shake in each direction.

///

public Vector3 shakeAmount = Vector3.one;

///

/// The amount to rotate in each direction.

///

public Vector3 rotationAmount = Vector3.one;

///

/// The initial distance for the first shake.

///

public float distance = 00.10f;

///

/// The speed multiplier for the shake.

///

public float speed = 50.00f;

///

/// The decay speed (between 0 and 1). Higher values will stop shaking sooner.

///

public float decay = 00.20f;

///

/// The modifier applied to speed in order to shake the GUI.

///

public float guiShakeModifier = 01.00f;

///

/// If true, multiplies the final shake speed by the time scale.

///

public bool multiplyByTimeScale = true;

// Shake rect (for GUI)

private Rect shakeRect;

// States

private bool shaking = false;

private bool cancelling = false;

internal class ShakeState

{

internal readonly Vector3 startPosition;

internal readonly Quaternion startRotation;

internal readonly Vector2 guiStartPosition;

internal Vector3 shakePosition;

internal Quaternion shakeRotation;

internal Vector2 guiShakePosition;

internal ShakeState(Vector3 position, Quaternion rotation, Vector2 guiPosition)

{

startPosition = position;

startRotation = rotation;

guiStartPosition = guiPosition;

shakePosition = position;

shakeRotation = rotation;

guiShakePosition = guiPosition;

}

}

private Dictionary> states = new Dictionary>();

private Dictionary shakeCount = new Dictionary();

// Minimum shake values

private const bool checkForMinimumValues = true;

private const float minShakeValue = 0.001f;

private const float minRotationValue = 0.001f;

#region Singleton

///

/// The Camera Shake singleton instance.

///

public static CameraShake instance;

private void OnEnable()

{

if (cameras.Count < 1)

{

if (camera)

cameras.Add(camera);

}

if (cameras.Count < 1)

{

if (Camera.main)

cameras.Add(Camera.main);

}

if (cameras.Count < 1)

{

Debug.LogError("Camera Shake: No cameras assigned in the inspector!");

}

instance = this;

}

#endregion

#region Static properties

public static bool isShaking

{

get

{

return instance.IsShaking();

}

}

public static bool isCancelling

{

get

{

return instance.IsCancelling();

}

}

#endregion

#region Static methods

public static void Shake()

{

instance.DoShake();

}

public static void Shake(int numberOfShakes, Vector3 shakeAmount, Vector3 rotationAmount, float distance, float speed, float decay, float guiShakeModifier, bool multiplyByTimeScale)

{

instance.DoShake(numberOfShakes, shakeAmount, rotationAmount, distance, speed, decay, guiShakeModifier, multiplyByTimeScale);

}

public static void Shake(System.Action callback)

{

instance.DoShake(callback);

}

public static void Shake(int numberOfShakes, Vector3 shakeAmount, Vector3 rotationAmount, float distance, float speed, float decay, float guiShakeModifier, bool multiplyByTimeScale, System.Action callback)

{

instance.DoShake(numberOfShakes, shakeAmount, rotationAmount, distance, speed, decay, guiShakeModifier, multiplyByTimeScale, callback);

}

public static void CancelShake()

{

instance.DoCancelShake();

}

public static void CancelShake(float time)

{

instance.DoCancelShake(time);

}

public static void BeginShakeGUI()

{

instance.DoBeginShakeGUI();

}

public static void EndShakeGUI()

{

instance.DoEndShakeGUI();

}

public static void BeginShakeGUILayout()

{

instance.DoBeginShakeGUILayout();

}

public static void EndShakeGUILayout()

{

instance.DoEndShakeGUILayout();

}

#endregion

#region Events

///

/// Occurs when a camera starts shaking.

///

public event System.Action cameraShakeStarted;

///

/// Occurs when a camera has completely stopped shaking and has been reset to its original position.

///

public event System.Action allCameraShakesCompleted;

#endregion

#region Public methods

public bool IsShaking()

{

return shaking;

}

public bool IsCancelling()

{

return cancelling;

}

public void DoShake()

{

Vector3 seed = Random.insideUnitSphere;

foreach(Camera cam in cameras)

{

StartCoroutine(DoShake_Internal(cam, seed, this.numberOfShakes, this.shakeAmount, this.rotationAmount, this.distance, this.speed, this.decay, this.guiShakeModifier, this.multiplyByTimeScale, null));

}

}

public void DoShake(int numberOfShakes, Vector3 shakeAmount, Vector3 rotationAmount, float distance, float speed, float decay, float guiShakeModifier, bool multiplyByTimeScale)

{

Vector3 seed = Random.insideUnitSphere;

foreach(Camera cam in cameras)

{

StartCoroutine(DoShake_Internal(cam, seed, numberOfShakes, shakeAmount, rotationAmount, distance, speed, decay, guiShakeModifier, multiplyByTimeScale, null));

}

}

public void DoShake(System.Action callback)

{

Vector3 seed = Random.insideUnitSphere;

foreach(Camera cam in cameras)

{

StartCoroutine(DoShake_Internal(cam, seed, this.numberOfShakes, this.shakeAmount, this.rotationAmount, this.distance, this.speed, this.decay, this.guiShakeModifier, this.multiplyByTimeScale, callback));

}

}

public void DoShake(int numberOfShakes, Vector3 shakeAmount, Vector3 rotationAmount, float distance, float speed, float decay, float guiShakeModifier, bool multiplyByTimeScale, System.Action callback)

{

Vector3 seed = Random.insideUnitSphere;

foreach(Camera cam in cameras)

{

StartCoroutine(DoShake_Internal(cam, seed, numberOfShakes, shakeAmount, rotationAmount, distance, speed, decay, guiShakeModifier, multiplyByTimeScale, callback));

}

}

public void DoCancelShake()

{

if (shaking && !cancelling)

{

shaking = false;

this.StopAllCoroutines();

foreach(Camera cam in cameras)

{

if (shakeCount.ContainsKey(cam))

{

shakeCount[cam] = 0;

}

ResetState(cam.transform, cam);

}

}

}

public void DoCancelShake(float time)

{

if (shaking && !cancelling)

{

this.StopAllCoroutines();

this.StartCoroutine(DoResetState(cameras, shakeCount, time));

}

}

public void DoBeginShakeGUI()

{

CheckShakeRect();

GUI.BeginGroup(shakeRect);

}

public void DoEndShakeGUI()

{

GUI.EndGroup();

}

public void DoBeginShakeGUILayout()

{

CheckShakeRect();

GUILayout.BeginArea(shakeRect);

}

public void DoEndShakeGUILayout()

{

GUILayout.EndArea();

}

#endregion

#region Private methods

private void OnDrawGizmosSelected()

{

foreach(Camera cam in cameras)

{

if (!cam)

continue;

if (IsShaking())

{

Vector3 offset = cam.worldToCameraMatrix.GetColumn(3);

offset.z *= -1;

offset = cam.transform.position + cam.transform.TransformPoint(offset);

Quaternion rot = QuaternionFromMatrix(cam.worldToCameraMatrix.inverse * Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(1,1,-1)));

Matrix4x4 matrix = Matrix4x4.TRS(offset, rot, cam.transform.lossyScale);

Gizmos.matrix = matrix;

}

else

{

Matrix4x4 matrix = Matrix4x4.TRS(cam.transform.position, cam.transform.rotation, cam.transform.lossyScale);

Gizmos.matrix = matrix;

}

Gizmos.DrawWireCube(Vector3.zero, shakeAmount);

Gizmos.color = Color.cyan;

if (cam.isOrthoGraphic)

{

Vector3 pos = new Vector3(0, 0, (cam.near + cam.far) / 2f);

Vector3 size = new Vector3(cam.orthographicSize / cam.aspect, cam.orthographicSize * 2f, cam.far - cam.near);

Gizmos.DrawWireCube(pos, size);

}

else

{

Gizmos.DrawFrustum(Vector3.zero, cam.fov, cam.far, cam.near, (.7f / cam.aspect));

}

}

}

private IEnumerator DoShake_Internal(Camera cam, Vector3 seed, int numberOfShakes, Vector3 shakeAmount, Vector3 rotationAmount, float distance, float speed, float decay, float guiShakeModifier, bool multiplyByTimeScale, System.Action callback)

{

// Wait for async cancel operations to complete

if (cancelling)

yield return null;

// Set random values

var mod1 = seed.x > .5f ? 1 : -1;

var mod2 = seed.y > .5f ? 1 : -1;

var mod3 = seed.z > .5f ? 1 : -1;

// First shake

if (!shaking)

{

shaking = true;

if (cameraShakeStarted != null)

cameraShakeStarted();

}

if (shakeCount.ContainsKey(cam))

shakeCount[cam]++;

else

shakeCount.Add(cam, 1);

// Pixel width is always based on the first camera

float pixelWidth = GetPixelWidth(cameras[0].transform, cameras[0]);

// Set other values

Transform cachedTransform = cam.transform;

Vector3 camOffset = Vector3.zero;

Quaternion camRot = Quaternion.identity;

int currentShakes = numberOfShakes;

float shakeDistance = distance;

float rotationStrength = 1;

float startTime = Time.time;

float scale = multiplyByTimeScale ? Time.timeScale : 1;

float pixelScale = pixelWidth * guiShakeModifier * scale;

Vector3 start1 = Vector2.zero;

Quaternion startR = Quaternion.identity;

Vector2 start2 = Vector2.zero;

ShakeState state = new ShakeState(cachedTransform.position, cachedTransform.rotation, new Vector2(shakeRect.x, shakeRect.y));

List stateList;

if (states.TryGetValue(cam, out stateList))

{

stateList.Add(state);

}

else

{

stateList = new List();

stateList.Add(state);

states.Add(cam, stateList);

}

// Main loop

while (currentShakes > 0)

{

if (checkForMinimumValues)

{

// Early break when rotation is less than the minimum value.

if (rotationAmount.sqrMagnitude != 0 && rotationStrength <= minRotationValue)

break;

// Early break when shake amount is less than the minimum value.

if (shakeAmount.sqrMagnitude != 0 && distance != 0 && shakeDistance <= minShakeValue)

break;

}

var timer = (Time.time - startTime) * speed;

state.shakePosition = start1 + new Vector3(

mod1 * Mathf.Sin(timer) * (shakeAmount.x * shakeDistance * scale),

mod2 * Mathf.Cos(timer) * (shakeAmount.y * shakeDistance * scale),

mod3 * Mathf.Sin(timer) * (shakeAmount.z * shakeDistance * scale));

state.shakeRotation = startR * Quaternion.Euler(

mod1 * Mathf.Cos(timer) * (rotationAmount.x * rotationStrength * scale),

mod2 * Mathf.Sin(timer) * (rotationAmount.y * rotationStrength * scale),

mod3 * Mathf.Cos(timer) * (rotationAmount.z * rotationStrength * scale));

state.guiShakePosition = new Vector2(

start2.x - (mod1 * Mathf.Sin(timer) * (shakeAmount.x * shakeDistance * pixelScale)),

start2.y - (mod2 * Mathf.Cos(timer) * (shakeAmount.y * shakeDistance * pixelScale)));

camOffset = GetGeometricAvg(stateList, true);

camRot = GetAvgRotation(stateList);

NormalizeQuaternion(ref camRot);

Matrix4x4 m = Matrix4x4.TRS(camOffset, camRot, new Vector3(1, 1, -1));

cam.worldToCameraMatrix = m * cachedTransform.worldToLocalMatrix;

var avg = GetGeometricAvg(stateList, false);

shakeRect.x = avg.x;

shakeRect.y = avg.y;

if (timer > Mathf.PI * 2)

{

startTime = Time.time;

shakeDistance *= (1 - Mathf.Clamp01(decay));

rotationStrength *= (1 - Mathf.Clamp01(decay));

currentShakes--;

}

yield return null;

}

// End conditions

shakeCount[cam]--;

// Last shake

if (shakeCount[cam] == 0)

{

shaking = false;

ResetState(cam.transform, cam);

if (allCameraShakesCompleted != null)

{

allCameraShakesCompleted();

}

}

else

{

stateList.Remove(state);

}

if (callback != null)

callback();

}

private Vector3 GetGeometricAvg(List states, bool position)

{

float x = 0, y = 0, z = 0, l = states.Count;

foreach(ShakeState state in states)

{

if (position)

{

x -= state.shakePosition.x;

y -= state.shakePosition.y;

z -= state.shakePosition.z;

}

else

{

x += state.guiShakePosition.x;

y += state.guiShakePosition.y;

}

}

return new Vector3(x / l, y / l, z / l);

}

private Quaternion GetAvgRotation(List states)

{

Quaternion avg = new Quaternion(0,0,0,0);

foreach(ShakeState state in states)

{

if (Quaternion.Dot (state.shakeRotation, avg) > 0)

{

avg.x += state.shakeRotation.x;

avg.y += state.shakeRotation.y;

avg.z += state.shakeRotation.z;

avg.w += state.shakeRotation.w;

}

else

{

avg.x += -state.shakeRotation.x;

avg.y += -state.shakeRotation.y;

avg.z += -state.shakeRotation.z;

avg.w += -state.shakeRotation.w;

}

}

var mag = Mathf.Sqrt(avg.x* avg.x + avg.y* avg.y + avg.z * avg.z + avg.w * avg.w);

if (mag > 0.0001f)

{

avg.x /= mag;

avg.y /= mag;

avg.z /= mag;

avg.w /= mag;

}

else

{

avg = states[0].shakeRotation;

}

return avg;

}

private void CheckShakeRect()

{

if (Screen.width != shakeRect.width || Screen.height != shakeRect.height)

{

shakeRect.width = Screen.width;

shakeRect.height = Screen.height;

}

}

private float GetPixelWidth(Transform cachedTransform, Camera cachedCamera)

{

var position = cachedTransform.position;

var screenPos = cachedCamera.WorldToScreenPoint(position - cachedTransform.forward * .01f);

var offset = Vector3.zero;

if (screenPos.x > 0)

offset = screenPos - Vector3.right;

else

offset = screenPos + Vector3.right;

if (screenPos.y > 0)

offset = screenPos - Vector3.up;

else

offset = screenPos + Vector3.up;

offset = cachedCamera.ScreenToWorldPoint(offset);

return 1f / (cachedTransform.InverseTransformPoint(position) - cachedTransform.InverseTransformPoint(offset)).magnitude;

}

private void ResetState(Transform cachedTransform, Camera cam)

{

cam.ResetWorldToCameraMatrix();

shakeRect.x = 0;

shakeRect.y = 0;

states[cam].Clear();

}

private List offsetCache = new List(10);

private List rotationCache = new List(10);

private IEnumerator DoResetState(List cameras, Dictionary shakeCount, float time)

{

offsetCache.Clear();

rotationCache.Clear();

foreach(Camera cam in cameras)

{

offsetCache.Add((Vector3)((cam.worldToCameraMatrix * cam.transform.worldToLocalMatrix.inverse).GetColumn(3)));

rotationCache.Add(QuaternionFromMatrix((cam.worldToCameraMatrix * cam.transform.worldToLocalMatrix.inverse).inverse * Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(1,1,-1))));

if (shakeCount.ContainsKey(cam))

{

shakeCount[cam] = 0;

}

states[cam].Clear();

}

float t = 0;

float x = shakeRect.x, y = shakeRect.y;

cancelling = true;

while (t < time)

{

int i = 0;

foreach(Camera cam in cameras)

{

Transform cachedTransform = cam.transform;

shakeRect.x = Mathf.Lerp(x, 0, t / time);

shakeRect.y = Mathf.Lerp(y, 0, t / time);

Vector3 pos = Vector3.Lerp(offsetCache[i], Vector3.zero, t / time);

Quaternion rot = Quaternion.Slerp(rotationCache[i], cachedTransform.rotation, t / time);

Matrix4x4 m = Matrix4x4.TRS(pos, rot, new Vector3(1, 1, -1));

cam.worldToCameraMatrix = m * cachedTransform.worldToLocalMatrix;

i++;

}

t += Time.deltaTime;

yield return null;

}

foreach(Camera cam in cameras)

{

cam.ResetWorldToCameraMatrix();

shakeRect.x = 0;

shakeRect.y = 0;

}

this.shaking = false;

this.cancelling = false;

}

#endregion

#region Quaternion helpers

private static Quaternion QuaternionFromMatrix(Matrix4x4 m)

{

return Quaternion.LookRotation(m.GetColumn(2), m.GetColumn(1));

}

private static void NormalizeQuaternion (ref Quaternion q)

{

float sum = 0;

for (int i = 0; i < 4; ++i)

sum += q[i] * q[i];

float magnitudeInverse = 1 / Mathf.Sqrt(sum);

for (int i = 0; i < 4; ++i)

q[i] *= magnitudeInverse;

}

#endregion

}

以上是 unity3D实现摄像机抖动特效 的全部内容, 来源链接: utcz.com/z/318161.html

回到顶部