using KitsuneCafe.Extension; using KitsuneCafe.ItemSystem; using KitsuneCafe.Sys; using KitsuneCafe.Sys.Attributes; using R3; using UnityEngine; namespace KitsuneCafe.Entity { public class EntityAnimator : MonoBehaviour { private const float epsilon = 0.05f; [SerializeField] private Animator animator; [SerializeField] private new Rigidbody rigidbody; [Header("Movement")] [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; [Header("Combat")] [SerializeField, AnimatorParam("animator")] private string readyParam; [SerializeField] private EquipmentInstance equipment; private void OnValidate() { if (animator == null) { animator = GetComponent(); } if (rigidbody == null) { rigidbody = GetComponent(); } if (motor == null) { motor = GetComponent(); } if (sprint == null) { sprint = GetComponent(); } if (equipment == null) { equipment = 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); Observable.FromEventHandler( e => equipment.Readied += e, e => equipment.Readied -= e ) .Select(eh => eh.e) .Subscribe(ready => animator.SetBool(readyParam, ready)) .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; } } } }