// Copyright (c) 2015 - 2023 Doozy Entertainment. All Rights Reserved. // This code can only be used under the standard Unity Asset Store End User License Agreement // A Copy of the EULA APPENDIX 1 is available at http://unity3d.com/company/legal/as_terms using System; using Doozy.Runtime.Common; using Doozy.Runtime.Reactor.Easings; using UnityEngine; using static UnityEngine.Mathf; namespace Doozy.Runtime.Reactor { [Serializable] public class ReactionSettings { public const int k_InfiniteLoops = -1; public const PlayMode k_PlayMode = PlayMode.Normal; public const EaseMode k_EaseMode = EaseMode.Ease; public const Ease k_Ease = Ease.Easy; public const float k_StartDelay = 0f; public const float k_Duration = 1f; public const int k_Loops = 0; public const float k_LoopDelay = 0f; public const float k_Strength = 1f; public const int k_Vibration = 8; public const float k_Elasticity = 1f; [SerializeField] private PlayMode PlayMode; /// /// Determines what type of computation is used when the reaction is playing /// public PlayMode playMode { get => PlayMode; set => PlayMode = value; } [SerializeField] private EaseMode EaseMode; /// /// Determines the evaluation method used to update the eased progress /// EaseMode.Ease - uses the set ease to evaluate the current reaction and update the eased progress /// EaseMode.AnimationCurve - uses the set curve to evaluate the current project and update the eased progress /// public EaseMode easeMode { get => EaseMode; set => EaseMode = value; } [SerializeField] private Ease Ease; /// /// Easing used to update the eased progress when the easeMode is set to EaseMode.Ease /// public Ease ease { get => Ease; set { Ease = value; EaseMode = EaseMode.Ease; } } private IEasing easing => EaseFactory.GetEase(Ease); [SerializeField] private AnimationCurve Curve; /// /// Animation curve used to update the eased progress when the easeMode is set to EaseMode.AnimationCurve /// public AnimationCurve curve { get => Curve; set { Curve = value; EaseMode = EaseMode.AnimationCurve; } } [SerializeField] private float StartDelay; /// /// Reaction start delay time interval /// public float startDelay { get => StartDelay; set => StartDelay = Max(0, value); } [SerializeField] private float Duration; /// /// Reaction playing time interval /// public float duration { get => Duration; set => Duration = Max(0, value); } [SerializeField] private int Loops; /// /// Number of times the reaction replays (restarts) /// -1 - infinite loops /// 0 - no loops (plays once) /// > 0 - replays (restarts) for the given number of loops /// public int loops { get => Loops; set => Loops = Max(-1, value); } [SerializeField] private float LoopDelay; /// /// Reaction delay between loops time interval /// public float loopDelay { get => LoopDelay; set => LoopDelay = Max(0, value); } [SerializeField] private bool UseRandomStartDelay; /// /// Determines if the reaction's start delay time interval is a fixed or a random value /// Default value: FALSE /// public bool useRandomStartDelay { get => UseRandomStartDelay; set => UseRandomStartDelay = value; } [SerializeField] private bool UseRandomDuration; /// /// Determines if the reaction's duration time interval is a fixed or a random value /// Default value: FALSE /// public bool useRandomDuration { get => UseRandomDuration; set => UseRandomDuration = value; } [SerializeField] private bool UseRandomLoops; /// /// Determines if the reaction should play for a fixed or a random number of loops /// Default value: FALSE /// public bool useRandomLoops { get => UseRandomLoops; set => UseRandomLoops = value; } [SerializeField] private bool UseRandomLoopDelay; /// /// Determines if the reaction's delay between loops interval is a fixed or a random value /// Default value: FALSE /// public bool useRandomLoopDelay { get => UseRandomLoopDelay; set => UseRandomLoopDelay = value; } [SerializeField] private RandomFloat RandomStartDelay = new RandomFloat(); /// /// Random start delay interval value, used if useRandomStartDelay is TRUE /// Returns a new random value (between min and max) /// public RandomFloat randomStartDelay { get => RandomStartDelay; set => RandomStartDelay = value; } [SerializeField] private RandomFloat RandomDuration = new RandomFloat(); /// /// Random duration interval value, used if useRandomDuration is TRUE /// Returns a new random value (between min and max) /// public RandomFloat randomDuration { get => RandomDuration; set => RandomDuration = value; } [SerializeField] private RandomInt RandomLoops = new RandomInt(); /// /// Random number of loops value, used if useRandomLoops is TRUE /// Returns a new random value (between min and max) /// public RandomInt randomLoops { get => RandomLoops; set => RandomLoops = value; } [SerializeField] private RandomFloat RandomLoopDelay = new RandomFloat(); /// /// Random delay between loops interval value, used if useRandomLoopDelay is TRUE /// Returns a new random value (between min and max) /// public RandomFloat randomLoopDelay { get => RandomLoopDelay; set => RandomLoopDelay = value; } [SerializeField] private float Strength; /// /// Multiplier applied to the current value for spring and shake play modes /// Default value: 1f /// public float strength { get => Strength; set => Strength = value; } [SerializeField] private int Vibration; /// /// Represents the minimum number of oscillations for spring and shake play modes /// Higher value means more oscillations during the reaction's duration /// public int vibration { get => Vibration; set => Vibration = Max(0, value); } [SerializeField] private float Elasticity; /// /// Spring elasticity controls how much the current value goes back beyond the start value when contracting /// 0 - current value does not go beyond the start value /// 1 - current value goes beyond the start value at maximum elastic force /// public float elasticity { get => Elasticity; set => Elasticity = Clamp01(value); } [SerializeField] private bool FadeOutShake; /// /// Fade out the shake animation, by easing the last 20% of cycles (shakes) into a semi-smooth transition /// public bool fadeOutShake { get => FadeOutShake; set => FadeOutShake = value; } /// Returns TRUE is useRandomLoops is true or if loops not 0 (zero) public bool hasLoops => useRandomLoops || loops != 0; /// /// Get the start delay value, for the reaction's current start delay setting /// useRandomStartDelay: TRUE - returns randomStartDelay value /// useRandomStartDelay: FALSE - returns startDelay value /// public float GetStartDelay() => useRandomStartDelay ? randomStartDelay.randomValue : startDelay; /// Set a random start delay interval [min,max] /// Min value /// Max value /// If TRUE, the reaction will use a random start delay value, in the given [min,max] interval public void SetRandomStartDelay(float min, float max, bool useRandomValue = true) { useRandomStartDelay = useRandomValue; min = Max(0, min); max = Max(0, max); randomStartDelay = new RandomFloat(min, max); if (!Approximately(min, max)) return; // min == max >>> makes no sense to use a random value useRandomStartDelay = false; startDelay = min; } /// /// Get the duration value, for the reaction's current duration setting /// useRandomDuration: TRUE - returns randomDuration value /// useRandomDuration: FALSE - returns duration value /// public float GetDuration() => useRandomDuration ? randomDuration.randomValue : duration; /// Set a random duration interval [min,max] /// Min value /// Max value /// If TRUE, the reaction will use a random duration value, in the given [min,max] interval public void SetRandomDuration(float min, float max, bool useRandomValue = true) { useRandomDuration = useRandomValue; min = Max(0, min); max = Max(0, max); randomDuration = new RandomFloat(min, max); if (!Approximately(min, max)) return; // min == max >>> makes no sense to use a random value useRandomDuration = false; duration = min; } /// /// Get the number of loops, the reaction will replay (restart) until it finishes /// useRandomLoops: TRUE - returns randomLoops value /// useRandomLoops: FALSE - returns loops value /// public int GetLoops() => useRandomLoops ? randomLoops.randomValue : loops; /// Set a random loops interval [min,max] /// Min value /// Max value /// If TRUE, the reaction will use a random loops value, in the given [min,max] interval public void SetRandomLoops(int min, int max, bool useRandomValue = true) { useRandomLoops = useRandomValue; min = Max(0, min); max = Max(1, max); randomLoops = new RandomInt(min, max); if (min != max) return; // min == max >>> makes no sense to use a random value useRandomLoops = false; loops = min; } /// /// Get the delay between loops delay value, for the reaction's current loop delay setting /// useRandomLoopDelay: TRUE - returns randomLoopDelay value /// useRandomLoopDelay: FALSE - returns loopDelay value /// public float GetLoopDelay() => useRandomLoopDelay ? randomLoopDelay.randomValue : loopDelay; /// Set a random loop delay interval [min,max] /// Min value /// Max value /// If TRUE, the reaction will use a random loop delay value, in the given [min,max] interval public void SetRandomLoopDelay(float min, float max, bool useRandomValue = true) { useRandomLoopDelay = useRandomValue; min = Max(0, min); max = Max(0, max); randomLoopDelay = new RandomFloat(min, max); if (!Approximately(min, max)) return; // min == max >>> makes no sense to use a random value useRandomLoopDelay = false; loopDelay = min; } public ReactionSettings() { Reset(); } public ReactionSettings(ReactionSettings other) { Reset(); playMode = other.playMode; curve = other.curve; ease = other.ease; easeMode = other.easeMode; startDelay = other.startDelay; duration = other.duration; loops = other.loops; loopDelay = other.loopDelay; strength = other.strength; vibration = other.vibration; elasticity = other.elasticity; UseRandomStartDelay = other.useRandomStartDelay; UseRandomDuration = other.useRandomDuration; UseRandomLoops = other.useRandomLoops; UseRandomLoopDelay = other.UseRandomLoopDelay; RandomStartDelay = new RandomFloat(other.randomStartDelay); RandomDuration = new RandomFloat(other.randomDuration); RandomLoops = new RandomInt(other.randomLoops); RandomLoopDelay = new RandomFloat(other.randomLoopDelay); } /// Reset settings to the default values public void Reset() { playMode = k_PlayMode; curve = AnimationCurve.Linear(0, 0, 1, 1); ease = k_Ease; easeMode = k_EaseMode; startDelay = k_StartDelay; duration = k_Duration; loops = k_Loops; loopDelay = k_LoopDelay; strength = k_Strength; vibration = k_Vibration; elasticity = k_Elasticity; UseRandomStartDelay = false; UseRandomDuration = false; UseRandomLoops = false; UseRandomLoopDelay = false; RandomStartDelay.Reset(); RandomDuration.Reset(); RandomLoops.Reset(); RandomLoopDelay.Reset(); } /// /// Validate settings /// public void Validate() { StartDelay = startDelay; Duration = duration; Loops = loops; LoopDelay = loopDelay; Strength = strength; Vibration = vibration; Elasticity = elasticity; } /// /// Get the calculated eased progress value for the given progress value /// /// Progress value public float CalculateEasedProgress(float progress) { switch (easeMode) { case EaseMode.Ease: return easing.Evaluate(progress); case EaseMode.AnimationCurve: return curve.Evaluate(progress); default: throw new ArgumentOutOfRangeException(); } } } }