canto/Assets/Scripts/Entity/MotorAnimator.cs

140 lines
3.7 KiB
C#

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<LandingForce>(
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<float> CurrentVelocity()
{
return Observable.EveryUpdate(UnityFrameProvider.FixedUpdate)
.Select(_ => rigidbody.linearVelocity.sqrMagnitude)
.DistinctUntilChanged()
.Select(Mathf.Sqrt);
}
private Observable<float> CurrentAnimationSpeed(Observable<float> speedSource)
{
var scaleFactor = motor.MaxSpeedSource.Select(maxSpeed => NormalizationFactor(maxSpeed, motor.DefaultMaxSpeed));
return speedSource
.CombineLatest(motor.MaxSpeedSource, scaleFactor, NormalizeSpeed)
.DistinctUntilChanged(FEqualityComparer<float>.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;
}
}
}
}