using R3; using UnityEngine; using KitsuneCafe.System; using KitsuneCafe.Extension; using KitsuneCafe.System.Attributes; namespace KitsuneCafe.Entity { public class EntityAnimator : MonoBehaviour { private const float epsilon = 0.05f; [SerializeField] private Animator animator; [SerializeField] private new Rigidbody rigidbody; [SerializeField] private Motor motor; [SerializeField] private SprintFeature sprint; [SerializeField, AnimatorParam("animator")] private string movementSpeedParameter; [SerializeField] private float movementSmoothTime = 0.1f; [SerializeField, AnimatorParam("animator")] private string sprintingParameter; [SerializeField, Range(0f, 1f)] private float walkAnimationParamMax = 0.5f; [SerializeField, Range(0f, 1f)] private float sprintAnimationParamMax = 1.0f; [SerializeField, AnimatorParam("animator")] private string readyParam; private void Reset() { animator = GetComponent(); rigidbody = GetComponent(); motor = GetComponent(); sprint = GetComponent(); } private void Awake() { var d = Disposable.CreateBuilder(); CurrentVelocity() .Compose(CurrentAnimationSpeed) .SmoothDamp( () => animator.GetFloat(movementSpeedParameter), movementSmoothTime, UnityTimeProvider.FixedUpdate ) .Subscribe(speed => { animator.SetFloat(movementSpeedParameter, speed); }) .AddTo(ref d); sprint.IsSprinting .DistinctUntilChanged() .Subscribe(isSprinting => { animator.SetBool(sprintingParameter, isSprinting); }) .AddTo(ref d); d.RegisterTo(destroyCancellationToken); } private Observable CurrentVelocity() { return Observable.EveryUpdate(UnityFrameProvider.FixedUpdate) .Select(_ => rigidbody.linearVelocity.sqrMagnitude) .DistinctUntilChanged() .Select(Mathf.Sqrt); } private Observable CurrentAnimationSpeed(Observable speedSource) { var scaleFactor = motor.MaxSpeedSource.Select(maxSpeed => NormalizationFactor(maxSpeed, motor.DefaultMaxSpeed)); return speedSource .CombineLatest(motor.MaxSpeedSource, scaleFactor, NormalizeSpeed) .DistinctUntilChanged(FEqualityComparer.Create(KMath.IsApproxEqual)); } private float NormalizationFactor(float maxSpeed, float defaultMaxSpeed) { return maxSpeed > defaultMaxSpeed ? sprintAnimationParamMax : walkAnimationParamMax; } private float NormalizeSpeed(float currentSpeed, float maxSpeed, float factor) { if (currentSpeed > maxSpeed && maxSpeed > epsilon) { return factor; } else { return Mathf.InverseLerp(0, maxSpeed, currentSpeed) * factor; } } } }