Files
jelito/Assets/jelycho/Code/Player/PlayerAnimator.cs
2025-10-08 21:07:47 +02:00

277 lines
8.8 KiB
C#

using System;
using System.Runtime.CompilerServices;
using Animancer;
using RebootReality.jelycho.Items;
using Unity.Mathematics;
using UnityEngine;
using UnityEvent = UnityEngine.Events.UnityEvent;
namespace RebootReality.jelycho.Player {
public struct PlayerLocomotionAnimatorParams {
public float VelocityForwardNormalized;
public float VelocityRightNormalized;
public float TurnVelocity;
public bool IsGrounded;
}
public class PlayerHandsAnimator {
enum State {
None,
Idle,
QuickAttack,
Charging,
ChargedIdle,
Charged,
ChargedUse
}
AnimancerLayer m_AnimancerLayer;
AnimancerState m_AnimancerState;
ItemHandsAnimationClipsSet m_ClipsSet;
State m_State;
public event Action OnQuickAttackAnimationFinished = delegate { };
public event Action OnCharged = delegate { };
public PlayerHandsAnimator(AnimancerLayer layer) {
m_AnimancerLayer = layer;
}
public void Tick(float deltaTime) {
if (m_AnimancerState == null) {
return;
}
switch (m_State) {
case State.QuickAttack: {
if (IsCurrentClipFinished()) {
// m_AnimancerState.Stop();
m_AnimancerState = null;
OnQuickAttackAnimationFinished?.Invoke();
}
break;
}
case State.Charging: {
if (IsCurrentClipFinished()) {
SetChargedIdle();
OnCharged?.Invoke();
}
break;
}
case State.ChargedUse: {
if (IsCurrentClipFinished()) {
SetIdle();
}
break;
}
}
}
public void UpdateClips(ItemHandsAnimationClipsSet clipsSet) {
m_ClipsSet = clipsSet;
if (clipsSet == null) {
m_State = State.None;
return;
}
SetIdle();
}
public void PlayQuickAttack(int combo) {
if (m_ClipsSet == null) {
return;
}
AnimationClip clip = m_ClipsSet.quickAttacks[combo % m_ClipsSet.quickAttacks.Length];
m_AnimancerState = m_AnimancerLayer.Play(clip, 0.1f);
m_State = State.QuickAttack;
}
public void SetIdle() {
if (m_ClipsSet == null) {
return;
}
m_AnimancerState = m_AnimancerLayer.Play(m_ClipsSet.idle, 0.1f);
m_State = State.Idle;
}
public void SetCharging() {
if (m_ClipsSet == null) {
return;
}
m_AnimancerState = m_AnimancerLayer.Play(m_ClipsSet.charging, 0.1f);
m_State = State.Charging;
}
public void SetChargedIdle() {
if (m_ClipsSet == null) {
return;
}
m_AnimancerState = m_AnimancerLayer.Play(m_ClipsSet.chargedIdle, 0.1f);
m_State = State.ChargedIdle;
}
public void PlayChargedUse() {
if (m_ClipsSet == null) {
return;
}
m_AnimancerState = m_AnimancerLayer.Play(m_ClipsSet.chargedUse, 0.1f);
m_State = State.ChargedUse;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
bool IsCurrentClipFinished() {
return m_AnimancerState.NormalizedTime >= 1.0f;
}
}
public class PlayerAnimator : MonoBehaviour {
[SerializeField] AnimancerComponent m_Animancer;
[SerializeField] AnimationClip m_IdleClip;
[SerializeField] TransitionAsset m_GroundLocomotion;
[SerializeField] StringAsset m_GroundLocomotionPropertyX;
[SerializeField] StringAsset m_GroundLocomotionPropertyY;
SmoothedVector2Parameter m_SmoothLocomotionDirection;
[SerializeField] TransitionAsset m_GroundTurn;
[SerializeField] StringAsset m_GroundTurnProperty;
SmoothedFloatParameter m_SmoothGroundTurn;
[SerializeField] AnimationClip m_InAirClip;
[SerializeField] AnimationClip m_KickClip;
[SerializeField] AvatarMask m_HandsMask;
[SerializeField] AvatarMask m_LegsMask;
AnimancerLayer m_BaseLayer;
AnimancerLayer m_LocomotionLegsLayer;
AnimancerLayer m_HandsLayer;
AnimancerLayer m_FullBodyLayer;
PlayerHandsAnimator m_Hands;
bool m_IsPlayingKickAnimation;
// @TODO: for some reason `SetLocomotionParams` is called before awake
bool m_IsReady = false;
public UnityEvent onQuickAttackFinished = new UnityEvent();
public UnityEvent onChargeReady = new UnityEvent();
void Awake() {
m_BaseLayer = m_Animancer.Layers[0];
m_BaseLayer.SetDebugName("Base Layer");
m_BaseLayer.Play(m_IdleClip);
m_LocomotionLegsLayer = m_Animancer.Layers[1];
m_LocomotionLegsLayer.SetDebugName("Locomotion Legs");
m_LocomotionLegsLayer.Mask = m_LegsMask;
m_SmoothLocomotionDirection = new SmoothedVector2Parameter(m_Animancer,
m_GroundLocomotionPropertyX,
m_GroundLocomotionPropertyY,
0.1f);
m_LocomotionLegsLayer.Play(m_GroundLocomotion);
m_SmoothGroundTurn = new SmoothedFloatParameter(m_Animancer,
m_GroundTurnProperty,
0.1f);
m_HandsLayer = m_Animancer.Layers[2];
m_HandsLayer.SetDebugName("Hands");
m_HandsLayer.Mask = m_HandsMask;
m_HandsLayer.SetWeight(0.0f);
m_Hands = new PlayerHandsAnimator(m_HandsLayer);
m_Hands.OnQuickAttackAnimationFinished += () => onQuickAttackFinished.Invoke();
m_Hands.OnCharged += () => onChargeReady.Invoke();
m_FullBodyLayer = m_Animancer.Layers[3];
m_FullBodyLayer.SetDebugName("Full body");
m_FullBodyLayer.SetWeight(0.0f);
m_IsReady = true;
}
void OnDestroy() {
m_SmoothLocomotionDirection.Dispose();
}
void Update() {
float dt = Time.deltaTime;
m_Hands.Tick(dt);
if (m_IsPlayingKickAnimation &&
m_FullBodyLayer.CurrentState.NormalizedTime >= 1.0f) {
m_FullBodyLayer.SetWeight(0.0f);
m_IsPlayingKickAnimation = false;
}
}
public void SetLocomotionParams(PlayerLocomotionAnimatorParams locomotionParams) {
if (!m_IsReady) {
return;
}
if (!locomotionParams.IsGrounded) {
m_LocomotionLegsLayer.Play(m_InAirClip, 0.1f);
return;
}
if (math.abs(locomotionParams.TurnVelocity) > 0.1f) {
m_SmoothGroundTurn.TargetValue = locomotionParams.TurnVelocity;
m_LocomotionLegsLayer.Play(m_GroundTurn, 0.1f);
} else {
var groundBlendDirection = new float2(locomotionParams.VelocityRightNormalized,
locomotionParams.VelocityForwardNormalized);
m_SmoothLocomotionDirection.TargetValue = groundBlendDirection;
m_LocomotionLegsLayer.Play(m_GroundLocomotion, 0.1f);
}
}
public void SetHandsAnimationSet(ItemHandsAnimationClipsSet clipsSet) {
if (clipsSet == null) {
m_HandsLayer.SetWeight(0.0f);
return;
}
m_HandsLayer.SetWeight(1.0f);
m_Hands.UpdateClips(clipsSet);
}
public void PlayQuickAttack(int combo) {
m_Hands.PlayQuickAttack(combo);
}
public void PlayHandsIdle() {
m_Hands.SetIdle();
}
public void PlayCharging() {
m_Hands.SetCharging();
}
public void PlayChargedUse() {
m_Hands.PlayChargedUse();
}
public void PlayKickAnimation() {
if (m_IsPlayingKickAnimation) {
return;
}
m_FullBodyLayer.Play(m_KickClip, 0.1f).Time = 0.0f;
m_IsPlayingKickAnimation = true;
}
}
}