当前位置: 移动技术网 > IT编程>开发语言>c# > unity3D实现摄像机抖动特效

unity3D实现摄像机抖动特效

2020年03月09日  | 移动技术网IT编程  | 我要评论

本文实例为大家分享了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
 }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网