using System.Threading; using KitsuneCafe.Extension; using KitsuneCafe.Sys; using R3; using UnityEngine; using UnityForge.PropertyDrawers; namespace KitsuneCafe.Entities { public class MotorAnimator : MonoBehaviour { private const float epsilon = 0.05f; [Header("Dependencies")] [SerializeField] private Animator animator; [SerializeField] private new Rigidbody rigidbody; [SerializeField] private Spring spring; [SerializeField] private Motor motor; [SerializeField] private AirMotor airMotor; [SerializeField] private SprintFeature sprint; [Header("Movement")] [SerializeField, AnimatorParameterName(AnimatorControllerParameterType.Float, "animator")] private string movementSpeedParameter; [SerializeField] private float movementSmoothTime = 0.1f; [SerializeField, AnimatorParameterName(AnimatorControllerParameterType.Bool, "animator")] private string sprintingParameter; [SerializeField, Range(0f, 1f)] private float walkAnimationParamMax = 0.5f; [SerializeField, Range(0f, 1f)] private float sprintAnimationParamMax = 1.0f; [SerializeField, AnimatorParameterName(AnimatorControllerParameterType.Bool, "animator")] private string fallParam; [SerializeField, AnimatorParameterName(AnimatorControllerParameterType.Bool, "animator")] private string hardLandingParam; private CancellationTokenSource disableCancellationTokenSource; private CancellationToken disableCancellationToken => disableCancellationTokenSource.Token; private void Reset() { TryGetComponent(out animator); TryGetComponent(out rigidbody); TryGetComponent(out spring); TryGetComponent(out motor); TryGetComponent(out airMotor); TryGetComponent(out sprint); } private void OnEnable() { disableCancellationTokenSource = new(); var d = Disposable.CreateBuilder(); CurrentSpeed() .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); Observable.FromEventHandler( e => airMotor.Landed += e, e => airMotor.Landed -= e ) .Select(eh => eh.e) .Where(force => force == LandingForce.Hard) .Subscribe(force => animator.SetTrigger(hardLandingParam)) .AddTo(ref d); airMotor.ObservePropertyChanged(x => x.IsFalling) .Subscribe(falling => animator.SetBool(fallParam, falling)) .AddTo(ref d); d.RegisterTo(disableCancellationToken); } private void OnDisable() { disableCancellationTokenSource.Cancel(); } private Observable CurrentSpeed() => CurrentVelocity() .Select(vel => vel.sqrMagnitude) .DistinctUntilChanged() .Select(Mathf.Sqrt); private Observable CurrentVelocity() => Observable.EveryUpdate(UnityFrameProvider.FixedUpdate) .Select(_ => rigidbody.linearVelocity); private Observable CurrentAnimationSpeed(Observable speedSource) => speedSource .CombineLatest(motor.MaxSpeedSource, AnimationScaleFactor(), NormalizeSpeed) .DistinctUntilChanged(FEqualityComparer.Create(KMath.IsApproxEqual)); private Observable AnimationScaleFactor() => motor.MaxSpeedSource.Select( maxSpeed => NormalizationFactor(maxSpeed, motor.DefaultMaxSpeed) ); private float NormalizationFactor(float maxSpeed, float defaultMaxSpeed) => maxSpeed > defaultMaxSpeed ? sprintAnimationParamMax : walkAnimationParamMax; private float NormalizeSpeed(float currentSpeed, float maxSpeed, float factor) => currentSpeed > maxSpeed && maxSpeed > epsilon ? factor : Mathf.InverseLerp(0, maxSpeed, currentSpeed) * factor; } }