using System.Collections; using System.Collections.Generic; using UnityEngine; using Cinemachine; using MoreMountains.Feedbacks; namespace MoreMountains.FeedbacksForThirdParty { /// /// Add this component to your Cinemachine Virtual Camera to have it shake when calling its ShakeCamera methods. /// [AddComponentMenu("More Mountains/Feedbacks/Shakers/Cinemachine/MMCinemachineCameraShaker")] [RequireComponent(typeof(CinemachineVirtualCamera))] public class MMCinemachineCameraShaker : MonoBehaviour { [Header("Settings")] /// the channel to receive events on [Tooltip("the channel to receive events on")] public int Channel = 0; /// The default amplitude that will be applied to your shakes if you don't specify one [Tooltip("The default amplitude that will be applied to your shakes if you don't specify one")] public float DefaultShakeAmplitude = .5f; /// The default frequency that will be applied to your shakes if you don't specify one [Tooltip("The default frequency that will be applied to your shakes if you don't specify one")] public float DefaultShakeFrequency = 10f; /// the amplitude of the camera's noise when it's idle [Tooltip("the amplitude of the camera's noise when it's idle")] [MMFReadOnly] public float IdleAmplitude; /// the frequency of the camera's noise when it's idle [Tooltip("the frequency of the camera's noise when it's idle")] [MMFReadOnly] public float IdleFrequency = 1f; /// the speed at which to interpolate the shake [Tooltip("the speed at which to interpolate the shake")] public float LerpSpeed = 5f; [Header("Test")] /// a duration (in seconds) to apply when testing this shake via the TestShake button [Tooltip("a duration (in seconds) to apply when testing this shake via the TestShake button")] public float TestDuration = 0.3f; /// the amplitude to apply when testing this shake via the TestShake button [Tooltip("the amplitude to apply when testing this shake via the TestShake button")] public float TestAmplitude = 2f; /// the frequency to apply when testing this shake via the TestShake button [Tooltip("the frequency to apply when testing this shake via the TestShake button")] public float TestFrequency = 20f; [MMFInspectorButton("TestShake")] public bool TestShakeButton; public virtual float GetTime() { return (_timescaleMode == TimescaleModes.Scaled) ? Time.time : Time.unscaledTime; } public virtual float GetDeltaTime() { return (_timescaleMode == TimescaleModes.Scaled) ? Time.deltaTime : Time.unscaledDeltaTime; } protected TimescaleModes _timescaleMode; protected Vector3 _initialPosition; protected Quaternion _initialRotation; protected Cinemachine.CinemachineBasicMultiChannelPerlin _perlin; protected Cinemachine.CinemachineVirtualCamera _virtualCamera; protected float _targetAmplitude; protected float _targetFrequency; private Coroutine _shakeCoroutine; /// /// On awake we grab our components /// protected virtual void Awake() { _virtualCamera = this.gameObject.GetComponent(); _perlin = _virtualCamera.GetCinemachineComponent(); } /// /// On Start we reset our camera to apply our base amplitude and frequency /// protected virtual void Start() { if (_perlin != null) { IdleAmplitude = _perlin.m_AmplitudeGain; IdleFrequency = _perlin.m_FrequencyGain; } _targetAmplitude = IdleAmplitude; _targetFrequency = IdleFrequency; } protected virtual void Update() { if (_perlin != null) { _perlin.m_AmplitudeGain = _targetAmplitude; _perlin.m_FrequencyGain = Mathf.Lerp(_perlin.m_FrequencyGain, _targetFrequency, GetDeltaTime() * LerpSpeed); } } /// /// Use this method to shake the camera for the specified duration (in seconds) with the default amplitude and frequency /// /// Duration. public virtual void ShakeCamera(float duration, bool infinite, bool useUnscaledTime = false) { StartCoroutine(ShakeCameraCo(duration, DefaultShakeAmplitude, DefaultShakeFrequency, infinite, useUnscaledTime)); } /// /// Use this method to shake the camera for the specified duration (in seconds), amplitude and frequency /// /// Duration. /// Amplitude. /// Frequency. public virtual void ShakeCamera(float duration, float amplitude, float frequency, bool infinite, bool useUnscaledTime = false) { if (_shakeCoroutine != null) { StopCoroutine(_shakeCoroutine); } _shakeCoroutine = StartCoroutine(ShakeCameraCo(duration, amplitude, frequency, infinite, useUnscaledTime)); } /// /// This coroutine will shake the /// /// The camera co. /// Duration. /// Amplitude. /// Frequency. protected virtual IEnumerator ShakeCameraCo(float duration, float amplitude, float frequency, bool infinite, bool useUnscaledTime) { _targetAmplitude = amplitude; _targetFrequency = frequency; _timescaleMode = useUnscaledTime ? TimescaleModes.Unscaled : TimescaleModes.Scaled; if (!infinite) { yield return new WaitForSeconds(duration); CameraReset(); } } /// /// Resets the camera's noise values to their idle values /// public virtual void CameraReset() { _targetAmplitude = IdleAmplitude; _targetFrequency = IdleFrequency; } public virtual void OnCameraShakeEvent(float duration, float amplitude, float frequency, float amplitudeX, float amplitudeY, float amplitudeZ, bool infinite, int channel, bool useUnscaledTime) { if ((channel != Channel) && (channel != -1) && (Channel != -1)) { return; } this.ShakeCamera(duration, amplitude, frequency, infinite, useUnscaledTime); } public virtual void OnCameraShakeStopEvent(int channel) { if ((channel != Channel) && (channel != -1) && (Channel != -1)) { return; } if (_shakeCoroutine != null) { StopCoroutine(_shakeCoroutine); } CameraReset(); } protected virtual void OnEnable() { MMCameraShakeEvent.Register(OnCameraShakeEvent); MMCameraShakeStopEvent.Register(OnCameraShakeStopEvent); } protected virtual void OnDisable() { MMCameraShakeEvent.Unregister(OnCameraShakeEvent); MMCameraShakeStopEvent.Unregister(OnCameraShakeStopEvent); } protected virtual void TestShake() { MMCameraShakeEvent.Trigger(TestDuration, TestAmplitude, TestFrequency, 0f, 0f, 0f, false, 0); } } }