// --------------------------------------------------------------------------------------------------------------------
//
// 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
}
}