本文实例为大家分享了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
}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
今夜才发现耍猴的太多