using KitsuneCafe.Extension; using KitsuneCafe.Sys; using KitsuneCafe.Sys.Attributes; using R3; using UnityEngine; 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; [Header("Movement")] [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 fallParam; [SerializeField, AnimatorParam("animator")] private string hardLandingParam; private void OnValidate() { this.TryGetComponentIfNull(ref animator); this.TryGetComponentIfNull(ref rigidbody); this.TryGetComponentIfNull(ref spring); this.TryGetComponentIfNull(ref motor); this.TryGetComponentIfNull(ref airMotor); this.TryGetComponentIfNull(ref sprint); } 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); 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(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; } } } }