unity3D实现摄像机抖动特效
本文实例为大家分享了unity3D实现摄像机抖动的具体代码,供大家参考,具体内容如下
摄像机抖动特效在需要的地方调用CameraShake.Shake()方法就可以
publicclassCameraShake:MonoBehaviour
{
///
///Thecamerastoshake.
///
publicListcameras=newList();
///
///Themaximumnumberofshakestoperform.
///
publicintnumberOfShakes=2;
///
///Theamounttoshakeineachdirection.
///
publicVector3shakeAmount=Vector3.one;
///
///Theamounttorotateineachdirection.
///
publicVector3rotationAmount=Vector3.one;
///
///Theinitialdistanceforthefirstshake.
///
publicfloatdistance=00.10f;
///
///Thespeedmultiplierfortheshake.
///
publicfloatspeed=50.00f;
///
///Thedecayspeed(between0and1).Highervalueswillstopshakingsooner.
///
publicfloatdecay=00.20f;
///
///ThemodifierappliedtospeedinordertoshaketheGUI.
///
publicfloatguiShakeModifier=01.00f;
///
///Iftrue,multipliesthefinalshakespeedbythetimescale.
///
publicboolmultiplyByTimeScale=true;
//Shakerect(forGUI)
privateRectshakeRect;
//States
privateboolshaking=false;
privateboolcancelling=false;
internalclassShakeState
{
internalreadonlyVector3startPosition;
internalreadonlyQuaternionstartRotation;
internalreadonlyVector2guiStartPosition;
internalVector3shakePosition;
internalQuaternionshakeRotation;
internalVector2guiShakePosition;
internalShakeState(Vector3position,Quaternionrotation,Vector2guiPosition)
{
startPosition=position;
startRotation=rotation;
guiStartPosition=guiPosition;
shakePosition=position;
shakeRotation=rotation;
guiShakePosition=guiPosition;
}
}
privateDictionary>states=newDictionary>();
privateDictionaryshakeCount=newDictionary();
//Minimumshakevalues
privateconstboolcheckForMinimumValues=true;
privateconstfloatminShakeValue=0.001f;
privateconstfloatminRotationValue=0.001f;
#regionSingleton
///
///TheCameraShakesingletoninstance.
///
publicstaticCameraShakeinstance;
privatevoidOnEnable()
{
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("CameraShake:Nocamerasassignedintheinspector!");
}
instance=this;
}
#endregion
#regionStaticproperties
publicstaticboolisShaking
{
get
{
returninstance.IsShaking();
}
}
publicstaticboolisCancelling
{
get
{
returninstance.IsCancelling();
}
}
#endregion
#regionStaticmethods
publicstaticvoidShake()
{
instance.DoShake();
}
publicstaticvoidShake(intnumberOfShakes,Vector3shakeAmount,Vector3rotationAmount,floatdistance,floatspeed,floatdecay,floatguiShakeModifier,boolmultiplyByTimeScale)
{
instance.DoShake(numberOfShakes,shakeAmount,rotationAmount,distance,speed,decay,guiShakeModifier,multiplyByTimeScale);
}
publicstaticvoidShake(System.Actioncallback)
{
instance.DoShake(callback);
}
publicstaticvoidShake(intnumberOfShakes,Vector3shakeAmount,Vector3rotationAmount,floatdistance,floatspeed,floatdecay,floatguiShakeModifier,boolmultiplyByTimeScale,System.Actioncallback)
{
instance.DoShake(numberOfShakes,shakeAmount,rotationAmount,distance,speed,decay,guiShakeModifier,multiplyByTimeScale,callback);
}
publicstaticvoidCancelShake()
{
instance.DoCancelShake();
}
publicstaticvoidCancelShake(floattime)
{
instance.DoCancelShake(time);
}
publicstaticvoidBeginShakeGUI()
{
instance.DoBeginShakeGUI();
}
publicstaticvoidEndShakeGUI()
{
instance.DoEndShakeGUI();
}
publicstaticvoidBeginShakeGUILayout()
{
instance.DoBeginShakeGUILayout();
}
publicstaticvoidEndShakeGUILayout()
{
instance.DoEndShakeGUILayout();
}
#endregion
#regionEvents
///
///Occurswhenacamerastartsshaking.
///
publiceventSystem.ActioncameraShakeStarted;
///
///Occurswhenacamerahascompletelystoppedshakingandhasbeenresettoitsoriginalposition.
///
publiceventSystem.ActionallCameraShakesCompleted;
#endregion
#regionPublicmethods
publicboolIsShaking()
{
returnshaking;
}
publicboolIsCancelling()
{
returncancelling;
}
publicvoidDoShake()
{
Vector3seed=Random.insideUnitSphere;
foreach(Cameracamincameras)
{
StartCoroutine(DoShake_Internal(cam,seed,this.numberOfShakes,this.shakeAmount,this.rotationAmount,this.distance,this.speed,this.decay,this.guiShakeModifier,this.multiplyByTimeScale,null));
}
}
publicvoidDoShake(intnumberOfShakes,Vector3shakeAmount,Vector3rotationAmount,floatdistance,floatspeed,floatdecay,floatguiShakeModifier,boolmultiplyByTimeScale)
{
Vector3seed=Random.insideUnitSphere;
foreach(Cameracamincameras)
{
StartCoroutine(DoShake_Internal(cam,seed,numberOfShakes,shakeAmount,rotationAmount,distance,speed,decay,guiShakeModifier,multiplyByTimeScale,null));
}
}
publicvoidDoShake(System.Actioncallback)
{
Vector3seed=Random.insideUnitSphere;
foreach(Cameracamincameras)
{
StartCoroutine(DoShake_Internal(cam,seed,this.numberOfShakes,this.shakeAmount,this.rotationAmount,this.distance,this.speed,this.decay,this.guiShakeModifier,this.multiplyByTimeScale,callback));
}
}
publicvoidDoShake(intnumberOfShakes,Vector3shakeAmount,Vector3rotationAmount,floatdistance,floatspeed,floatdecay,floatguiShakeModifier,boolmultiplyByTimeScale,System.Actioncallback)
{
Vector3seed=Random.insideUnitSphere;
foreach(Cameracamincameras)
{
StartCoroutine(DoShake_Internal(cam,seed,numberOfShakes,shakeAmount,rotationAmount,distance,speed,decay,guiShakeModifier,multiplyByTimeScale,callback));
}
}
publicvoidDoCancelShake()
{
if(shaking&&!cancelling)
{
shaking=false;
this.StopAllCoroutines();
foreach(Cameracamincameras)
{
if(shakeCount.ContainsKey(cam))
{
shakeCount[cam]=0;
}
ResetState(cam.transform,cam);
}
}
}
publicvoidDoCancelShake(floattime)
{
if(shaking&&!cancelling)
{
this.StopAllCoroutines();
this.StartCoroutine(DoResetState(cameras,shakeCount,time));
}
}
publicvoidDoBeginShakeGUI()
{
CheckShakeRect();
GUI.BeginGroup(shakeRect);
}
publicvoidDoEndShakeGUI()
{
GUI.EndGroup();
}
publicvoidDoBeginShakeGUILayout()
{
CheckShakeRect();
GUILayout.BeginArea(shakeRect);
}
publicvoidDoEndShakeGUILayout()
{
GUILayout.EndArea();
}
#endregion
#regionPrivatemethods
privatevoidOnDrawGizmosSelected()
{
foreach(Cameracamincameras)
{
if(!cam)
continue;
if(IsShaking())
{
Vector3offset=cam.worldToCameraMatrix.GetColumn(3);
offset.z*=-1;
offset=cam.transform.position+cam.transform.TransformPoint(offset);
Quaternionrot=QuaternionFromMatrix(cam.worldToCameraMatrix.inverse*Matrix4x4.TRS(Vector3.zero,Quaternion.identity,newVector3(1,1,-1)));
Matrix4x4matrix=Matrix4x4.TRS(offset,rot,cam.transform.lossyScale);
Gizmos.matrix=matrix;
}
else
{
Matrix4x4matrix=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)
{
Vector3pos=newVector3(0,0,(cam.near+cam.far)/2f);
Vector3size=newVector3(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));
}
}
}
privateIEnumeratorDoShake_Internal(Cameracam,Vector3seed,intnumberOfShakes,Vector3shakeAmount,Vector3rotationAmount,floatdistance,floatspeed,floatdecay,floatguiShakeModifier,boolmultiplyByTimeScale,System.Actioncallback)
{
//Waitforasynccanceloperationstocomplete
if(cancelling)
yieldreturnnull;
//Setrandomvalues
varmod1=seed.x>.5f?1:-1;
varmod2=seed.y>.5f?1:-1;
varmod3=seed.z>.5f?1:-1;
//Firstshake
if(!shaking)
{
shaking=true;
if(cameraShakeStarted!=null)
cameraShakeStarted();
}
if(shakeCount.ContainsKey(cam))
shakeCount[cam]++;
else
shakeCount.Add(cam,1);
//Pixelwidthisalwaysbasedonthefirstcamera
floatpixelWidth=GetPixelWidth(cameras[0].transform,cameras[0]);
//Setothervalues
TransformcachedTransform=cam.transform;
Vector3camOffset=Vector3.zero;
QuaternioncamRot=Quaternion.identity;
intcurrentShakes=numberOfShakes;
floatshakeDistance=distance;
floatrotationStrength=1;
floatstartTime=Time.time;
floatscale=multiplyByTimeScale?Time.timeScale:1;
floatpixelScale=pixelWidth*guiShakeModifier*scale;
Vector3start1=Vector2.zero;
QuaternionstartR=Quaternion.identity;
Vector2start2=Vector2.zero;
ShakeStatestate=newShakeState(cachedTransform.position,cachedTransform.rotation,newVector2(shakeRect.x,shakeRect.y));
ListstateList;
if(states.TryGetValue(cam,outstateList))
{
stateList.Add(state);
}
else
{
stateList=newList();
stateList.Add(state);
states.Add(cam,stateList);
}
//Mainloop
while(currentShakes>0)
{
if(checkForMinimumValues)
{
//Earlybreakwhenrotationislessthantheminimumvalue.
if(rotationAmount.sqrMagnitude!=0&&rotationStrength<=minRotationValue)
break;
//Earlybreakwhenshakeamountislessthantheminimumvalue.
if(shakeAmount.sqrMagnitude!=0&&distance!=0&&shakeDistance<=minShakeValue)
break;
}
vartimer=(Time.time-startTime)*speed;
state.shakePosition=start1+newVector3(
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=newVector2(
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(refcamRot);
Matrix4x4m=Matrix4x4.TRS(camOffset,camRot,newVector3(1,1,-1));
cam.worldToCameraMatrix=m*cachedTransform.worldToLocalMatrix;
varavg=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--;
}
yieldreturnnull;
}
//Endconditions
shakeCount[cam]--;
//Lastshake
if(shakeCount[cam]==0)
{
shaking=false;
ResetState(cam.transform,cam);
if(allCameraShakesCompleted!=null)
{
allCameraShakesCompleted();
}
}
else
{
stateList.Remove(state);
}
if(callback!=null)
callback();
}
privateVector3GetGeometricAvg(Liststates,boolposition)
{
floatx=0,y=0,z=0,l=states.Count;
foreach(ShakeStatestateinstates)
{
if(position)
{
x-=state.shakePosition.x;
y-=state.shakePosition.y;
z-=state.shakePosition.z;
}
else
{
x+=state.guiShakePosition.x;
y+=state.guiShakePosition.y;
}
}
returnnewVector3(x/l,y/l,z/l);
}
privateQuaternionGetAvgRotation(Liststates)
{
Quaternionavg=newQuaternion(0,0,0,0);
foreach(ShakeStatestateinstates)
{
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;
}
}
varmag=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;
}
returnavg;
}
privatevoidCheckShakeRect()
{
if(Screen.width!=shakeRect.width||Screen.height!=shakeRect.height)
{
shakeRect.width=Screen.width;
shakeRect.height=Screen.height;
}
}
privatefloatGetPixelWidth(TransformcachedTransform,CameracachedCamera)
{
varposition=cachedTransform.position;
varscreenPos=cachedCamera.WorldToScreenPoint(position-cachedTransform.forward*.01f);
varoffset=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);
return1f/(cachedTransform.InverseTransformPoint(position)-cachedTransform.InverseTransformPoint(offset)).magnitude;
}
privatevoidResetState(TransformcachedTransform,Cameracam)
{
cam.ResetWorldToCameraMatrix();
shakeRect.x=0;
shakeRect.y=0;
states[cam].Clear();
}
privateListoffsetCache=newList(10);
privateListrotationCache=newList(10);
privateIEnumeratorDoResetState(Listcameras,DictionaryshakeCount,floattime)
{
offsetCache.Clear();
rotationCache.Clear();
foreach(Cameracamincameras)
{
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,newVector3(1,1,-1))));
if(shakeCount.ContainsKey(cam))
{
shakeCount[cam]=0;
}
states[cam].Clear();
}
floatt=0;
floatx=shakeRect.x,y=shakeRect.y;
cancelling=true;
while(t