using UnityEngine; namespace Flexalon { /// /// The curve animator applies a curve the the position, rotation, and scale /// of the object. The curve is restarted each time the layout position changes. /// This is ideal for scenarios in which the layout position does not change often. /// [AddComponentMenu("Flexalon/Flexalon Curve Animator"), HelpURL("https://www.flexalon.com/docs/animators")] public class FlexalonCurveAnimator : MonoBehaviour, TransformUpdater { private FlexalonNode _node; private RectTransform _rectTransform; private bool _animateInWorldSpace = true; /// Determines if the animation should be performed in world space. public bool AnimateInWorldSpace { get => _animateInWorldSpace; set { _animateInWorldSpace = value; } } [SerializeField] private AnimationCurve _curve = AnimationCurve.Linear(0, 0, 1, 1); /// The curve to apply. Should begin at 0 and end at 1. public AnimationCurve Curve { get => _curve; set { _curve = value; } } [SerializeField] private bool _animatePosition = true; /// Determines if the position should be animated. public bool AnimatePosition { get => _animatePosition; set { _animatePosition = value; } } [SerializeField] private bool _animateRotation = true; /// Determines if the rotation should be animated. public bool AnimateRotation { get => _animateRotation; set { _animateRotation = value; } } [SerializeField] private bool _animateScale = true; /// Determines if the scale should be animated. public bool AnimateScale { get => _animateScale; set { _animateScale = value; } } private Vector3 _startPosition; private Quaternion _startRotation; private Vector3 _startScale; private Vector2 _startRectSize; private Vector3 _endPosition; private Quaternion _endRotation; private Vector3 _endScale; private Vector2 _endRectSize; private float _positionTime; private float _rotationTime; private float _scaleTime; private float _rectSizeTime; private Vector3 _fromPosition; private Quaternion _fromRotation; private Vector3 _fromScale; private Vector2 _fromRectSize; void OnEnable() { _startPosition = _endPosition = new Vector3(float.NaN, float.NaN, float.NaN); _startRotation = _endRotation = new Quaternion(float.NaN, float.NaN, float.NaN, float.NaN); _startScale = _endScale = new Vector3(float.NaN, float.NaN, float.NaN); _positionTime = _rotationTime = _scaleTime = 0; _rectTransform = (transform is RectTransform) ? (RectTransform)transform : null; _node = Flexalon.GetOrCreateNode(gameObject); _node.SetTransformUpdater(this); } void OnDisable() { _node?.SetTransformUpdater(null); _node = null; } /// public void PreUpdate(FlexalonNode node) { _fromPosition = transform.position; _fromRotation = transform.rotation; _fromScale = transform.lossyScale; _fromRectSize = _rectTransform?.rect.size ?? Vector2.zero; } /// public bool UpdatePosition(FlexalonNode node, Vector3 position) { var newEndPosition = position; var newStartPosition = transform.localPosition; if (_animateInWorldSpace) { newEndPosition = transform.parent ? transform.parent.localToWorldMatrix.MultiplyPoint(position) : position;; newStartPosition = _fromPosition; } if (newEndPosition != _endPosition) { _startPosition = newStartPosition; _endPosition = newEndPosition; _positionTime = 0; } _positionTime += Time.smoothDeltaTime; if (!_animatePosition || _positionTime > _curve.keys[_curve.keys.Length - 1].time) { transform.localPosition = position; _endPosition = new Vector3(float.NaN, float.NaN, float.NaN); return true; } else { var newPosition = Vector3.Lerp(_startPosition, _endPosition, _curve.Evaluate(_positionTime)); if (_animateInWorldSpace) { transform.position = newPosition; } else { transform.localPosition = newPosition; } return false; } } /// public bool UpdateRotation(FlexalonNode node, Quaternion rotation) { var newEndRotation = rotation; var newStartRotation = transform.localRotation; if (_animateInWorldSpace) { newEndRotation = transform.parent ? transform.parent.rotation * rotation : rotation;; newStartRotation = _fromRotation; } if (newEndRotation != _endRotation) { _startRotation = newStartRotation; _endRotation = newEndRotation; _rotationTime = 0; } _rotationTime += Time.smoothDeltaTime; if (!_animateRotation || _rotationTime > _curve.keys[_curve.keys.Length - 1].time) { transform.localRotation = rotation; _endRotation = new Quaternion(float.NaN, float.NaN, float.NaN, float.NaN); return true; } else { var newRotation = Quaternion.Slerp(_startRotation, _endRotation, _curve.Evaluate(_rotationTime)); if (_animateInWorldSpace) { transform.rotation = newRotation; } else { transform.localRotation = newRotation; } return false; } } /// public bool UpdateScale(FlexalonNode node, Vector3 scale) { var newEndScale = scale; var newStartScale = transform.localScale; if (_animateInWorldSpace) { newEndScale = transform.parent ? Math.Mul(scale, transform.parent.lossyScale) : scale; newStartScale = _fromScale; } if (newEndScale != _endScale) { _startScale = newStartScale; _endScale = newEndScale; _scaleTime = 0; } _scaleTime += Time.smoothDeltaTime; if (!_animateScale || _scaleTime > _curve.keys[_curve.keys.Length - 1].time) { transform.localScale = scale; _endScale = new Vector3(float.NaN, float.NaN, float.NaN); return true; } else { var newScale = Vector3.Lerp(_startScale, _endScale, _curve.Evaluate(_scaleTime)); if (_animateInWorldSpace) { transform.localScale = transform.parent ? Math.Div(newScale, transform.parent.lossyScale) : newScale; } else { transform.localScale = newScale; } return false; } } /// public bool UpdateRectSize(FlexalonNode node, Vector2 size) { if (size != _endRectSize) { _startRectSize = _fromRectSize; _endRectSize = size; _rectSizeTime = 0; } _rectSizeTime += Time.smoothDeltaTime; bool done = !_animateScale || _rectSizeTime > _curve.keys[_curve.keys.Length - 1].time; var newSize = done ? size : Vector2.Lerp(_startRectSize, _endRectSize, _curve.Evaluate(_rectSizeTime)); _rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, newSize.x); _rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, newSize.y); if (done) { _endRectSize = new Vector2(float.NaN, float.NaN); } return done; } } }