using UnityEngine; namespace CameraShake { public class PerlinShake : ICameraShake { readonly Params pars; readonly Envelope envelope; public IAmplitudeController AmplitudeController; Vector2[] seeds; float time; Vector3? sourcePosition; float norm; /// /// Creates an instance of PerlinShake. /// /// Parameters of the shake. /// Maximum amplitude of the shake. /// World position of the source of the shake. /// Pass true if you want to control amplitude manually. public PerlinShake( Params parameters, float maxAmplitude = 1, Vector3? sourcePosition = null, bool manualStrengthControl = false) { pars = parameters; envelope = new Envelope(pars.envelope, maxAmplitude, manualStrengthControl ? Envelope.EnvelopeControlMode.Manual : Envelope.EnvelopeControlMode.Auto); AmplitudeController = envelope; this.sourcePosition = sourcePosition; } public Displacement CurrentDisplacement { get; private set; } public bool IsFinished { get; private set; } public void Initialize(Vector3 cameraPosition, Quaternion cameraRotation) { seeds = new Vector2[pars.noiseModes.Length]; norm = 0; for (int i = 0; i < seeds.Length; i++) { seeds[i] = Random.insideUnitCircle * 20; norm += pars.noiseModes[i].amplitude; } } public void Update(float deltaTime, Vector3 cameraPosition, Quaternion cameraRotation) { if (envelope.IsFinished) { IsFinished = true; return; } time += deltaTime; envelope.Update(deltaTime); Displacement disp = Displacement.Zero; for (int i = 0; i < pars.noiseModes.Length; i++) { disp += pars.noiseModes[i].amplitude / norm * SampleNoise(seeds[i], pars.noiseModes[i].freq); } CurrentDisplacement = envelope.Intensity * Displacement.Scale(disp, pars.strength); if (sourcePosition != null) CurrentDisplacement *= Attenuator.Strength(pars.attenuation, sourcePosition.Value, cameraPosition); } private Displacement SampleNoise(Vector2 seed, float freq) { Vector3 position = new Vector3( Mathf.PerlinNoise(seed.x + time * freq, seed.y), Mathf.PerlinNoise(seed.x, seed.y + time * freq), Mathf.PerlinNoise(seed.x + time * freq, seed.y + time * freq)); position -= Vector3.one * 0.5f; Vector3 rotation = new Vector3( Mathf.PerlinNoise(-seed.x - time * freq, -seed.y), Mathf.PerlinNoise(-seed.x, -seed.y - time * freq), Mathf.PerlinNoise(-seed.x - time * freq, -seed.y - time * freq)); rotation -= Vector3.one * 0.5f; return new Displacement(position, rotation); } [System.Serializable] public class Params { /// /// Strength of the shake for each axis. /// [Tooltip("Strength of the shake for each axis.")] public Displacement strength = new Displacement(Vector3.zero, new Vector3(2, 2, 0.8f)); /// /// Layers of perlin noise with different frequencies. /// [Tooltip("Layers of perlin noise with different frequencies.")] public NoiseMode[] noiseModes = { new NoiseMode(12, 1) }; /// /// Strength over time. /// [Tooltip("Strength of the shake over time.")] public Envelope.EnvelopeParams envelope; /// /// How strength falls with distance from the shake source. /// [Tooltip("How strength falls with distance from the shake source.")] public Attenuator.StrengthAttenuationParams attenuation; } [System.Serializable] public struct NoiseMode { public NoiseMode(float freq, float amplitude) { this.freq = freq; this.amplitude = amplitude; } /// /// Frequency multiplier for the noise. /// [Tooltip("Frequency multiplier for the noise.")] public float freq; /// /// Amplitude of the mode. /// [Tooltip("Amplitude of the mode.")] [Range(0, 1)] public float amplitude; } } }