using UnityEngine;
namespace CameraShake
{
///
/// Controls strength of the shake over time.
///
public class Envelope : IAmplitudeController
{
readonly EnvelopeParams pars;
readonly EnvelopeControlMode controlMode;
float amplitude;
float targetAmplitude;
float sustainEndTime;
bool finishWhenAmplitudeZero;
bool finishImmediately;
EnvelopeState state;
///
/// Creates an Envelope instance.
///
/// Envelope parameters.
/// Pass Auto for a single shake, or Manual for controlling strength manually.
public Envelope(EnvelopeParams pars, float initialTargetAmplitude, EnvelopeControlMode controlMode)
{
this.pars = pars;
this.controlMode = controlMode;
SetTarget(initialTargetAmplitude);
}
///
/// The value by which you want to multiply shake displacement.
///
public float Intensity { get; private set; }
public bool IsFinished
{
get
{
if (finishImmediately) return true;
return (finishWhenAmplitudeZero || controlMode == EnvelopeControlMode.Auto)
&& amplitude <= 0 && targetAmplitude <= 0;
}
}
public void Finish()
{
finishWhenAmplitudeZero = true;
SetTarget(0);
}
public void FinishImmediately()
{
finishImmediately = true;
}
///
/// Update is called every frame by the shake.
///
public void Update(float deltaTime)
{
if (IsFinished) return;
if (state == EnvelopeState.Increase)
{
if (pars.attack > 0)
amplitude += deltaTime * pars.attack;
if (amplitude > targetAmplitude || pars.attack <= 0)
{
amplitude = targetAmplitude;
state = EnvelopeState.Sustain;
if (controlMode == EnvelopeControlMode.Auto)
sustainEndTime = Time.time + pars.sustain;
}
}
else
{
if (state == EnvelopeState.Decrease)
{
if (pars.decay > 0)
amplitude -= deltaTime * pars.decay;
if (amplitude < targetAmplitude || pars.decay <= 0)
{
amplitude = targetAmplitude;
state = EnvelopeState.Sustain;
}
}
else
{
if (controlMode == EnvelopeControlMode.Auto && Time.time > sustainEndTime)
{
SetTarget(0);
}
}
}
amplitude = Mathf.Clamp01(amplitude);
Intensity = Power.Evaluate(amplitude, pars.degree);
}
public void SetTargetAmplitude(float value)
{
if (controlMode == EnvelopeControlMode.Manual && !finishWhenAmplitudeZero)
{
SetTarget(value);
}
}
private void SetTarget(float value)
{
targetAmplitude = Mathf.Clamp01(value);
state = targetAmplitude > amplitude ? EnvelopeState.Increase : EnvelopeState.Decrease;
}
[System.Serializable]
public class EnvelopeParams
{
///
/// How fast the amplitude rises.
///
[Tooltip("How fast the amplitude increases.")]
public float attack = 10;
///
/// How long in seconds the amplitude holds a maximum value.
///
[Tooltip("How long in seconds the amplitude holds maximum value.")]
public float sustain = 0;
///
/// How fast the amplitude falls.
///
[Tooltip("How fast the amplitude decreases.")]
public float decay = 1f;
///
/// Power in which the amplitude is raised to get intensity.
///
[Tooltip("Power in which the amplitude is raised to get intensity.")]
public Degree degree = Degree.Cubic;
}
public enum EnvelopeControlMode { Auto, Manual }
public enum EnvelopeState { Sustain, Increase, Decrease }
}
public interface IAmplitudeController
{
///
/// Sets value to which amplitude will move over time.
///
void SetTargetAmplitude(float value);
///
/// Sets amplitude to zero and finishes the shake when zero is reached.
///
void Finish();
///
/// Immediately finishes the shake.
///
void FinishImmediately();
}
}