Created
October 29, 2023 18:29
-
-
Save Donnotron666/d02d2b5392833c048faaf5ef87348bee to your computer and use it in GitHub Desktop.
Shake Controller
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Core.Utils; | |
using System; | |
using System.Collections.Generic; | |
using UnityEngine; | |
namespace Core.Movement | |
{ | |
public class ShakeController | |
{ | |
private List<Shake> ActiveShakes = new List<Shake>(); | |
public bool Empty => ActiveShakes.Count == 0; | |
private float Accum = 0f; | |
public float ReadSpeed { | |
get { | |
return Tuning.CameraShakeReadSpeed; | |
} | |
} | |
public PrototypeTuning Tuning { | |
get { | |
return PrototypeTuning.Instance; | |
} | |
} | |
public void Add(float transAmt, float rotAmt, float maxDistance = .5f) | |
{ | |
Add(new Shake(transAmt, rotAmt, 2f, maxDistance)); | |
} | |
public void Add(Shake shake) | |
{ | |
ActiveShakes.Add(shake); | |
} | |
float RotationAccum; | |
public Vector3 TransAccum = Vector3.zero; | |
Queue<Shake> RemoveBuffer = new Queue<Shake>(); | |
public void Update(float dt) | |
{ | |
Accum += dt; | |
foreach( var shake in ActiveShakes) | |
{ | |
shake.Consume(dt, Accum, out float thisRot, out Vector3 thisTrans); | |
RotationAccum += thisRot; | |
TransAccum += thisTrans; | |
if (shake.IsComplete) | |
{ | |
RemoveBuffer.Enqueue(shake); | |
} | |
} | |
while( RemoveBuffer.Count > 0) | |
{ | |
ActiveShakes.Remove(RemoveBuffer.Dequeue()); | |
} | |
} | |
public void Reset() | |
{ | |
ClearBuffer(); | |
ClearShakes(); | |
} | |
private Vector3 LastApplied = Vector3.zero; | |
public void ClearBuffer() | |
{ | |
RotationAccum = 0f; | |
LastApplied = TransAccum; | |
TransAccum = Vector3.zero; | |
} | |
public void ClearShakes() | |
{ | |
ActiveShakes.Clear(); | |
} | |
public Vector3 FlushToBuffer(Vector3 buffer) | |
{ | |
var aux = TransAccum; | |
var ret = buffer + aux;// - LastApplied; | |
ClearBuffer(); | |
return ret; | |
} | |
public void FlushShakeToTransform(Transform tfm, float dt) => FlushShakeToTransform(tfm, dt, Quaternion.identity); | |
public void FlushShakeToTransform(Transform tfm, float dt, Quaternion restingRotation) | |
{ | |
//Application and recovery | |
if (RotationAccum != 0) | |
{ | |
tfm.localRotation = restingRotation * Quaternion.AngleAxis(RotationAccum, Vector3.forward); | |
} | |
else | |
{ | |
tfm.localRotation = Quaternion.SlerpUnclamped(tfm.localRotation, restingRotation, dt * Tuning.ShakeReturnSpeed); | |
} | |
tfm.localPosition = tfm.localPosition + TransAccum;//(tfm.localPosition + (TransAccum - LastApplied)); | |
ClearBuffer(); | |
} | |
} | |
[Serializable] | |
public class Shake | |
{ | |
public float TransIntensity; | |
public float RotIntensity; | |
public float Duration; | |
public float MaxAngle = 5f; | |
//[HideInInspector] | |
public float Speed = 30f; | |
[HideInInspector] | |
public float Scalar = 1f; | |
[HideInInspector] | |
public float Accum = 0f; | |
[HideInInspector] | |
public float seedAccum = 0f; | |
public float MaxDistance = .5f; | |
public Shake() | |
{ | |
Scalar = 1f; | |
Speed = 30f; | |
} | |
public Shake(float amt, float rotAmt, float duration, float maxDistance) | |
{ | |
this.TransIntensity = amt; | |
this.RotIntensity = rotAmt; | |
this.Duration = duration; | |
this.MaxDistance = maxDistance; | |
} | |
public Shake(float amt, float rotAmt, float duration, float speed, float maxDistance) | |
{ | |
this.TransIntensity = amt; | |
this.RotIntensity = rotAmt; | |
this.Duration = duration; | |
this.Speed = speed; | |
this.MaxDistance = maxDistance; | |
} | |
public Shake Clone() => new Shake(TransIntensity, RotIntensity, Duration, MaxDistance); | |
protected PrototypeTuning Tuning { | |
get { | |
return PrototypeTuning.Instance; | |
} | |
} | |
public float RandomFloat(float seed) | |
{ | |
var alpha = Mathf.PerlinNoise(seed, seedAccum); | |
return Mathf.Lerp(-1f, 1f, alpha); | |
} | |
public virtual void Consume(float dt, float seedOffset, out float rotation, out Vector3 trans) | |
{ | |
Accum += dt; | |
if (RotIntensity > 0) | |
{ | |
seedAccum += dt * Mathf.Pow(RotIntensity, Tuning.ShakeFrequencyDecay) * this.Speed; | |
var angle = MaxAngle * RandomFloat(seedOffset + Tuning.ShakeRotSeed); | |
RotIntensity = (RotIntensity - Mathf.Clamp01(dt * Tuning.ShakeIntensityDecay * (RotIntensity + Tuning.ShakeDurationDecay))) * Scalar; | |
rotation = angle; | |
} else | |
{ | |
rotation = 0f; | |
} | |
if (TransIntensity > 0) | |
{ | |
var accumScalar = Mathf.Pow(TransIntensity, Tuning.ShakeFrequencyDecay) * this.Speed; | |
seedAccum += dt * accumScalar; | |
var x = TransIntensity * MaxDistance * RandomFloat(seedOffset + Tuning.ShakeTransSeedX); | |
var y = TransIntensity * MaxDistance * RandomFloat(seedOffset + Tuning.ShakeTransSeedY); | |
trans = new Vector3(x, y); | |
TransIntensity = Mathf.Clamp01(TransIntensity - (dt * Tuning.ShakeIntensityDecay * (TransIntensity + Tuning.ShakeDurationDecay))) * Scalar; | |
} else | |
{ | |
trans = Vector3.zero; | |
} | |
} | |
public bool IsComplete => Accum >= Duration; | |
public void Kill() | |
{ | |
this.Duration = 0f; | |
} | |
public Shake Scale(float alpha) | |
{ | |
this.Scalar = alpha; | |
return this; | |
} | |
} | |
[Serializable] | |
public class ConstantShake : Shake | |
{ | |
public ConstantShake() | |
{ | |
Scalar = 1f; | |
Speed = 30f; | |
} | |
new public ConstantShake Clone() => new ConstantShake(this); | |
public ConstantShake(Shake other) : this(other.TransIntensity, other.RotIntensity, other.Duration, other.Speed, other.MaxDistance) | |
{ | |
} | |
public ConstantShake(float amt, float rotAmt, float duration, float maxDistance) : base(amt, rotAmt, duration, maxDistance) | |
{ | |
} | |
public ConstantShake(float amt, float rotAmt, float duration, float shakeSpeed, float maxDistance) : base(amt, rotAmt, duration, shakeSpeed, maxDistance) | |
{ | |
} | |
public void Copy(Shake other) | |
{ | |
this.TransIntensity = other.TransIntensity; | |
this.RotIntensity = other.RotIntensity; | |
this.Scalar = other.Scalar; | |
this.Duration = other.Duration; | |
this.Speed = other.Speed; | |
} | |
public override void Consume(float dt, float offset, out float rotation, out Vector3 trans) | |
{ | |
this.Duration -= dt; | |
if (IsComplete) | |
{ | |
trans = Vector3.zero; | |
rotation = 0f; | |
return; | |
} | |
if (RotIntensity > 0) | |
{ | |
seedAccum += dt * Mathf.Pow(RotIntensity, Tuning.ShakeFrequencyDecay) * this.Speed; | |
var angle = this.MaxAngle * RandomFloat(offset + 100); | |
rotation = angle * Scalar; | |
} | |
else | |
{ | |
rotation = 0f; | |
} | |
if (TransIntensity > 0) | |
{ | |
var accumScalar = Mathf.Pow(TransIntensity, Tuning.ShakeFrequencyDecay) * this.Speed; | |
seedAccum += dt * accumScalar; | |
var x = MaxDistance * RandomFloat(offset + Tuning.ShakeTransSeedX) * Scalar; | |
var y = MaxDistance * RandomFloat(offset + Tuning.ShakeTransSeedY) * Scalar; | |
trans = new Vector3(x, y); | |
} | |
else | |
{ | |
trans = Vector3.zero; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment