unity3D实现摄像机抖动特效
程序员文章站
2023-02-20 20:07:42
本文实例为大家分享了unity3d实现摄像机抖动的具体代码,供大家参考,具体内容如下
摄像机抖动特效 在需要的地方调用camerashake.shake()方法就可以
pu...
本文实例为大家分享了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 }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。