// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) VRMADA, All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- using System; using UltimateXR.Animation.Interpolation; using UltimateXR.Core.Components; using UnityEngine; namespace UltimateXR.Animation { /// /// Base class to create components that animate properties. /// Animation components should support two main ways of usage: /// /// Adding and setting up component using Unity's editor. /// Adding and setting up component through scripting at runtime. /// /// /// Type of animated component public abstract class UxrAnimatedComponent : UxrComponent where T : UxrAnimatedComponent { #region Inspector Properties/Serialized Fields [SerializeField] private UxrAnimationMode _animationMode = UxrAnimationMode.None; [SerializeField] private float _valueSpeedDurationSeconds; [SerializeField] private Vector4 _valueSpeed; [SerializeField] private Vector4 _valueStart; [SerializeField] private Vector4 _valueEnd; [SerializeField] private Vector4 _valueDisabled; [SerializeField] private UxrInterpolationSettings _interpolationSettings; [SerializeField] private float _valueNoiseTimeStart; [SerializeField] private float _valueNoiseDuration; [SerializeField] private Vector4 _valueNoiseValueStart; [SerializeField] private Vector4 _valueNoiseValueEnd; [SerializeField] private Vector4 _valueNoiseValueMin; [SerializeField] private Vector4 _valueNoiseValueMax; [SerializeField] private Vector4 _valueNoiseFrequency; [SerializeField] private Vector4 _valueNoiseOffset; [SerializeField] private bool _useUnscaledTime; #endregion #region Public Types & Data /// /// Called when the animation finished. /// public event Action Finished; /// /// Gets the current animation time in seconds. The animation time is the scaled or unscaled time relative to the time /// the component was enabled. /// public float AnimationTime => CurrentTime - _startTime; /// /// Gets the animation mode. /// public UxrAnimationMode AnimationMode { get => _animationMode; protected set => _animationMode = value; } /// /// Gets whether the animation finished. /// public bool HasFinished { get; private set; } /// /// Gets or sets the increment per second when the animation mode is set to . /// public Vector4 Speed { get => _valueSpeed; set => _valueSpeed = value; } /// /// Gets or sets the animation duration in seconds when the animation mode is set to /// . /// Durations of 0 or less than 0 will be considered as infinite duration. /// public float SpeedDurationSeconds { get => _valueSpeedDurationSeconds; set => _valueSpeedDurationSeconds = value; } /// /// Gets or sets the start value when the animation mode is set to . /// public Vector4 InterpolatedValueStart { get => _valueStart; set => _valueStart = value; } /// /// Gets or sets the end value when the animation mode is set to . /// public Vector4 InterpolatedValueEnd { get => _valueEnd; set => _valueEnd = value; } /// /// Gets or sets the value to set when the component is disabled, when the animation mode is set to /// . /// public Vector4 InterpolatedValueWhenDisabled { get => _valueDisabled; set => _valueDisabled = value; } /// /// Gets or sets the interpolation settings when the animation mode is set to /// . /// public UxrInterpolationSettings InterpolationSettings { get => _interpolationSettings; set => _interpolationSettings = value; } /// /// Gets or sets the noise min value when the animation mode is set to . /// public Vector4 NoiseValueMin { get => _valueNoiseValueMin; set => _valueNoiseValueMin = value; } /// /// Gets or sets the noise max value when the animation mode is set to . /// public Vector4 NoiseValueMax { get => _valueNoiseValueMax; set => _valueNoiseValueMax = value; } /// /// Gets or sets the start time when the animation mode is set to . /// public float NoiseTimeStart { get => _valueNoiseTimeStart; set => _valueNoiseTimeStart = value; } /// /// Gets or sets the animation duration in seconds when the animation mode is set to /// . /// public float NoiseDurationSeconds { get => _valueNoiseDuration; set => _valueNoiseDuration = value; } /// /// Gets or sets the start value when the animation mode is set to . /// public Vector4 NoiseValueStart { get => _valueNoiseValueStart; set => _valueNoiseValueStart = value; } /// /// Gets or sets the end value when the animation mode is set to . /// public Vector4 NoiseValueEnd { get => _valueNoiseValueEnd; set => _valueNoiseValueEnd = value; } /// /// Gets or sets the noise frequency when the animation mode is set to . /// public Vector4 NoiseFrequency { get => _valueNoiseFrequency; set => _valueNoiseFrequency = value; } /// /// Gets or sets the noise offset when the animation mode is set to . /// public Vector4 NoiseOffset { get => _valueNoiseOffset; set => _valueNoiseOffset = value; } /// /// Gets or sets whether to use the unscaled time ( instead of /// . /// public bool UseUnscaledTime { get => _useUnscaledTime; set => _useUnscaledTime = value; } #endregion #region Public Methods /// /// Stops the animation on an object if it has an component currently attached. /// /// Target GameObject /// Whether to reset the animated component to the state before the animation started public static void Stop(GameObject gameObject, bool restoreOriginal = true) { T anim = gameObject.GetComponent(); if (anim) { anim.Stop(restoreOriginal); } } /// /// Stops the animation on an object if it has an component currently attached. /// /// Whether to reset the animated component to the state before the animation started public void Stop(bool restoreOriginal = true) { HasFinished = true; if (restoreOriginal) { RestoreOriginalValue(); } } #endregion #region Unity /// /// Called each time the object is enabled. Reset timer and set the curve state to unfinished. /// protected override void OnEnable() { base.OnEnable(); StartTimer(); } /// /// Called each time the object is disabled. /// protected override void OnDisable() { base.OnDisable(); if (AnimationMode == UxrAnimationMode.Interpolate) { SetParameterValue(InterpolatedValueWhenDisabled); } else if (AnimationMode == UxrAnimationMode.Noise) { SetParameterValue(NoiseValueEnd); } } /// /// Updates the animation. /// private void Update() { if (HasFinished) { return; } if (AnimationMode == UxrAnimationMode.Speed) { Vector4 value = GetParameterValue(); value += Speed * (_useUnscaledTime ? Time.unscaledDeltaTime : Time.deltaTime); SetParameterValue(value); if (_valueSpeedDurationSeconds > 0.0f) { if (AnimationTime >= _valueSpeedDurationSeconds) { HasFinished = true; OnFinished(this as T); } } } else if (AnimationMode == UxrAnimationMode.Interpolate) { Vector4 value = UxrInterpolator.Interpolate(_valueStart, _valueEnd, AnimationTime, InterpolationSettings); SetParameterValue(value); if (InterpolationSettings.CheckInterpolationHasFinished(AnimationTime)) { SetParameterValue(_valueEnd); HasFinished = true; OnFinished(this as T); } } else if (AnimationMode == UxrAnimationMode.Noise) { if (AnimationTime < NoiseTimeStart) { SetParameterValue(NoiseValueStart); } else if (AnimationTime > NoiseTimeStart + NoiseDurationSeconds) { SetParameterValue(NoiseValueEnd); HasFinished = true; OnFinished(this as T); } else { float tX = Mathf.PerlinNoise(_valueNoiseOffset[0] + AnimationTime * _valueNoiseFrequency[0], _valueNoiseOffset[0]); float tY = Mathf.PerlinNoise(_valueNoiseOffset[1] + AnimationTime * _valueNoiseFrequency[1], _valueNoiseOffset[1]); float tZ = Mathf.PerlinNoise(_valueNoiseOffset[2] + AnimationTime * _valueNoiseFrequency[2], _valueNoiseOffset[2]); float tW = Mathf.PerlinNoise(_valueNoiseOffset[3] + AnimationTime * _valueNoiseFrequency[3], _valueNoiseOffset[3]); SetParameterValue(new Vector4(Mathf.Lerp(_valueNoiseValueMin[0], _valueNoiseValueMax[0], tX), Mathf.Lerp(_valueNoiseValueMin[1], _valueNoiseValueMax[1], tY), Mathf.Lerp(_valueNoiseValueMin[2], _valueNoiseValueMax[2], tZ), Mathf.Lerp(_valueNoiseValueMin[3], _valueNoiseValueMax[3], tW))); } } } #endregion #region Event Trigger Methods /// /// Called when the animation finished. /// /// Animation that finished protected virtual void OnFinished(T anim) { Finished?.Invoke(anim); } #endregion #region Protected Methods /// /// Converts a float value to a Vector4. Internally Vector4 values are used for everything but some animations only /// require to store a float value. The x component will be used to store the value. /// /// Float value to store /// Vector4 storing the float value in the x component. protected static Vector4 ToVector4(float v) { return new Vector4(v, 0.0f, 0.0f, 0.0f); } /// /// Restores the animated component to the state before the animation started. /// protected abstract void RestoreOriginalValue(); /// /// Gets the current parameter value /// /// /// Vector4 containing the value. This value may not use all components depending on which parameter it is /// animating. /// protected abstract Vector4 GetParameterValue(); /// /// Sets the parameter value /// /// /// Vector4 containing the value. This value may not use all components depending on which parameter it is animating. /// protected abstract void SetParameterValue(Vector4 value); /// /// (Re)Starts the animation timer. /// protected void StartTimer() { HasFinished = false; _startTime = CurrentTime; } #endregion #region Private Types & Data /// /// Gets the current time in seconds. It computes the correct time, either /// or , depending on the animation configuration. /// private float CurrentTime { get { if (InterpolationSettings != null && AnimationMode == UxrAnimationMode.Interpolate) { return InterpolationSettings.UseUnscaledTime ? Time.unscaledTime : Time.time; } return _useUnscaledTime ? Time.unscaledTime : Time.time; } } private float _startTime; #endregion } }