From d2ab30e7a69bfe7efda63ae75812207377917bd3 Mon Sep 17 00:00:00 2001 From: miepzerino <o.skotnik@gmail.com> Date: Sun, 30 Mar 2025 18:50:27 +0000 Subject: [PATCH] Merge branch 'Flexalon-UI-Layouts' into develop --- Assets/Flexalon/Runtime/Animators/FlexalonCurveAnimator.cs | 261 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 261 insertions(+), 0 deletions(-) diff --git a/Assets/Flexalon/Runtime/Animators/FlexalonCurveAnimator.cs b/Assets/Flexalon/Runtime/Animators/FlexalonCurveAnimator.cs new file mode 100644 index 0000000..50493cd --- /dev/null +++ b/Assets/Flexalon/Runtime/Animators/FlexalonCurveAnimator.cs @@ -0,0 +1,261 @@ +using UnityEngine; + +namespace Flexalon +{ + /// <summary> + /// 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. + /// </summary> + [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; + /// <summary> Determines if the animation should be performed in world space. </summary> + public bool AnimateInWorldSpace + { + get => _animateInWorldSpace; + set { _animateInWorldSpace = value; } + } + + [SerializeField] + private AnimationCurve _curve = AnimationCurve.Linear(0, 0, 1, 1); + /// <summary> The curve to apply. Should begin at 0 and end at 1. </summary> + public AnimationCurve Curve + { + get => _curve; + set { _curve = value; } + } + + [SerializeField] + private bool _animatePosition = true; + /// <summary> Determines if the position should be animated. </summary> + public bool AnimatePosition + { + get => _animatePosition; + set { _animatePosition = value; } + } + + [SerializeField] + private bool _animateRotation = true; + /// <summary> Determines if the rotation should be animated. </summary> + public bool AnimateRotation + { + get => _animateRotation; + set { _animateRotation = value; } + } + + [SerializeField] + private bool _animateScale = true; + /// <summary> Determines if the scale should be animated. </summary> + 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; + } + + /// <inheritdoc /> + public void PreUpdate(FlexalonNode node) + { + _fromPosition = transform.position; + _fromRotation = transform.rotation; + _fromScale = transform.lossyScale; + _fromRectSize = _rectTransform?.rect.size ?? Vector2.zero; + } + + /// <inheritdoc /> + 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; + } + } + + /// <inheritdoc /> + 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; + } + } + + /// <inheritdoc /> + 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; + } + } + + /// <inheritdoc /> + 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; + } + } +} \ No newline at end of file -- Gitblit v1.9.3