unity3D实现摄像机抖动特效
作者:Zander_zhang 发布时间:2022-09-20 12:10:42
标签: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
}
来源:https://blog.csdn.net/zjw1349547081/article/details/44452191


猜你喜欢
- 本文实例讲述了Android编程设计模式之原型模式。分享给大家供大家参考,具体如下:一、介绍原型模式是一个创建型的模式。原型二字表明了该模型
- 在研究性能的时候,完全可以使用Stopwatch计时器计算一项技术的效率。但是有时想知道某想技术的性能的时候,又常常想不起可以运用Stopw
- 在编写程序时经常会使用到调用可执行程序的情况,本文将简单介绍C#调用exe的方法。在C#中,通过Process类来进行进程操作。 Proce
- 本文实例形式介绍了VB.NET中TextBox的智能感知实现方法,功能非常实用,具体如下:该实例主要实现:在TextBox中键入字符,可以智
- 1. Dubbo是什么?Dubbo是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。简单的说,
- C#中Invoke的用法()invoke和begininvoke 区别一直对invoke和begininvoke的使用和概念比较混乱,这两天
- 编写RedisConfig首先我们要明白RedisConfig中需要包含什么,首先看看我们直接使用RedisTemplate的问题,我们就知
- 本文实例为大家分享了抢红包源码,供大家参考,具体内容如下1. 正确获取红包流程2. 软件介绍2.1 效果图:2.2 功能介绍2.2.1 账号
- 实践过程效果代码public partial class Form1 : Form{ public Form1()
- 一、Mybatis1、mybatis-config.xml<?xml version="1.0" encoding
- 在upload.jsp页面中将多个文件域对象命名为相同的名字,这样在action中就可以将多个文件域解析成一个数组,数组的大小就是文件域的个
- 面试题:1.如何保证多线程下 i++ 结果正确?2.一个线程如果出现了运行时异常会怎么样?3.一个线程运行时发生异常会怎样?为了避免临界区的
- 为了学习数据库,重装了系统,之前前一直在用eclipse,现在准备换成myeclipse,这之前当然需要重新设置环境变量,顺手写下有关jdk
- 项目数据库中出现许多值为中括号[]的数据,测试报了bug,经过排查是因为使用了json-lib 的jar包导致。json-lib在将xml字
- 1. 前言现在很多应用都有小悬浮窗的功能,比如看直播的时候,通过Home键返回桌面,直播的小窗口仍可以在屏幕上显示。下面将介绍下悬浮窗的的一
- 一、选择结构大纲if单选择结构if双选择结构if多选择结构嵌套的if结构switch多选择结构二、if单选择结构我们很多时候需要去判断一个东
- 1 前言ATMS 即 ActivityTaskManagerService,用于管理 Activity 及其容器(任务、堆栈、显示等)。AT
- 一般使用过UCWEB-Android版的人都应该对其特殊的menu有一定的印象,把menu做成Tab-Menu(支持分页的Menu),可以容
- 在spring的注解 @RequestMapping 之下可以直接获取 HttpServletRequest 来获得诸如request he
- 一、用法:list集合中contains() 用于判断集合中 是否 包含指定的元素。list会将括号内的元素和list中存在的元素进行逐个比