canto/Assets/Scripts/Entity/EntityAnimator.cs
2025-08-14 19:11:32 -04:00

148 lines
4.2 KiB
C#

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<Animator>();
}
if (rigidbody == null)
{
rigidbody = GetComponent<Rigidbody>();
}
if (motor == null)
{
motor = GetComponent<Motor>();
}
if (sprint == null)
{
sprint = GetComponent<SprintFeature>();
}
if (equipment == null)
{
equipment = GetComponent<EquipmentInstance>();
}
}
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<bool>(
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<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;
}
}
}
}