animancer

This commit is contained in:
2025-10-08 21:07:47 +02:00
parent 35f1337446
commit 2fab3007ae
13 changed files with 159 additions and 171 deletions

View File

@@ -31,7 +31,6 @@ namespace RebootReality.jelycho.Player {
[SerializeField] float m_RunCameraBobbing = 0.5f;
[SerializeField] float m_IdleCameraBobbing = 0.0f;
[SerializeField] float m_CameraBobbingTransitionSpeed = 5.0f;
[SerializeField] float m_TurnTransitionSpeed = 5.0f;
float m_TargetCameraBobbing = 0.0f;
float m_CurrentCameraBobbing = 0.0f;
@@ -49,7 +48,6 @@ namespace RebootReality.jelycho.Player {
[SerializeField] float m_CharacterRotateFastSpeed = 720.0f;
float m_CharacterTurnVelocity = 0.0f;
float m_CharacterTurnVelocitySmooth = 0.0f;
[Header("Animations")]
[SerializeField] int m_HandsLayerIndex;
@@ -847,7 +845,9 @@ namespace RebootReality.jelycho.Player {
float3 targetCharacterForward = math.normalize(LookDirection.With(y: 0.0f));
float3 currentCharacterForward = math.normalize(m_CharacterForwardTransform.forward.With(y: 0.0f));
float angleDeg = Mathf.DeltaAngle(m_Camera.Yaw, m_Locomotion.YawRotation);
// float angleDeg = Mathf.DeltaAngle(m_Camera.Yaw, m_Locomotion.YawRotation);
float angleRad = math.acos(math.clamp(math.dot(targetCharacterForward, currentCharacterForward) / (math.length(targetCharacterForward) * math.length(currentCharacterForward)), -1f, 1f));
float angleDeg = math.degrees(angleRad);
bool rotateCharacter = false;
float rotateCharacterSpeed = m_CharacterRotateSpeed;
@@ -870,13 +870,14 @@ namespace RebootReality.jelycho.Player {
if (rotateCharacter) {
m_CharacterTurnVelocity = rotateCharacterSpeed * dt;
m_CharacterTurnVelocitySmooth = m_CharacterTurnVelocity;
var newForward = Vector3.RotateTowards(currentCharacterForward,
targetCharacterForward,
math.radians(m_CharacterTurnVelocity),
0.0f);
m_CharacterForwardTransform.forward = newForward;
} else {
m_CharacterTurnVelocity = 0.0f;
}
// Aim Target adjustment
@@ -991,20 +992,16 @@ namespace RebootReality.jelycho.Player {
if (math.abs(forwardNormalized) > 0.01f ||
math.abs(rightNormalized) > 0.01f ||
!m_Locomotion.IsGrounded) {
m_CharacterTurnVelocitySmooth = 0.0f;
m_CharacterTurnVelocity = 0.0f;
}
var locomotionParams = new PlayerLocomotionAnimatorParams {
IsGrounded = m_Locomotion.IsGrounded,
VelocityForwardNormalized = forwardNormalized,
VelocityRightNormalized = rightNormalized,
TurnVelocity = m_CharacterTurnVelocitySmooth
TurnVelocity = math.clamp(m_CharacterTurnVelocity, -1, 1)
};
m_PlayerAnimator.SetLocomotionParams(locomotionParams);
m_CharacterTurnVelocitySmooth = Mathf.MoveTowards(m_CharacterTurnVelocitySmooth,
0.0f,
m_TurnTransitionSpeed * Time.deltaTime);
}
}

View File

@@ -1,12 +1,9 @@
using System;
using System.Runtime.CompilerServices;
using Animancer;
using RebootKit.Engine.Animations;
using RebootReality.jelycho.Items;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Animations;
using UnityEngine.Playables;
using UnityEvent = UnityEngine.Events.UnityEvent;
namespace RebootReality.jelycho.Player {
@@ -17,117 +14,6 @@ namespace RebootReality.jelycho.Player {
public bool IsGrounded;
}
[Serializable]
public class BasicCharacterLocomotionReAnimatorNode : IReAnimatorNode {
[field: SerializeField] public string Name { get; private set; }
[SerializeField] AnimationClip m_IdleClip;
[SerializeField] AnimationClip m_RunForwardClip;
[SerializeField] AnimationClip m_RunBackwardsClip;
[SerializeField] AnimationClip m_StrafeRightClip;
[SerializeField] AnimationClip m_StrafeLeftClip;
[SerializeField] AnimationClip m_TurnRightClip;
[SerializeField] AnimationClip m_TurnLeftClip;
[SerializeField] float m_TransitionSpeed = 5.0f;
[SerializeField, Range(0.0f, 1.0f)] float m_ForceIdleMagnitudeThreshold = 0.2f;
AnimationMixerPlayable m_Mixer;
float2 m_TargetInput;
float2 m_CurrentInput;
float m_Turning;
public void Tick(float deltaTime) {
if (m_TransitionSpeed > 0.0f) {
m_CurrentInput = Vector2.MoveTowards(m_CurrentInput,
m_TargetInput,
m_TransitionSpeed * deltaTime);
} else {
m_CurrentInput = m_TargetInput;
}
if (math.length(m_CurrentInput) <= m_ForceIdleMagnitudeThreshold) {
for (int i = 0; i < 7; ++i) {
m_Mixer.SetInputWeight(i, 0.0f);
}
float turnWeight = math.clamp(math.abs(m_Turning), 0.0f, 1.0f);
if (m_Turning > 0.1f) {
m_Mixer.SetInputWeight(5, turnWeight);
} else if (m_Turning < -0.1f) {
m_Mixer.SetInputWeight(6, turnWeight);
}
m_Mixer.SetInputWeight(0, 1.0f - turnWeight);
return;
}
float inputMagnitude = math.length(m_CurrentInput);
float2 inputNormalized = math.normalizesafe(m_CurrentInput);
inputMagnitude = math.min(1.0f, inputMagnitude);
float forwardWeight = math.max(0.0f, math.dot(inputNormalized, new float2(0, 1)) * inputMagnitude);
float backwardsWeight = math.max(0.0f, math.dot(inputNormalized, new float2(0, -1)) * inputMagnitude);
float rightWeight = math.max(0.0f, math.dot(inputNormalized, new float2(1, 0)) * inputMagnitude);
float leftWeight = math.max(0.0f, math.dot(inputNormalized, new float2(-1, 0)) * inputMagnitude);
float totalWeight = forwardWeight + backwardsWeight + rightWeight + leftWeight;
if (totalWeight > 1.0f) {
forwardWeight /= totalWeight;
backwardsWeight /= totalWeight;
rightWeight /= totalWeight;
leftWeight /= totalWeight;
totalWeight = 1.0f;
}
float idleWeight = math.max(0.0f, 1.0f - totalWeight);
m_Mixer.SetInputWeight(0, idleWeight);
m_Mixer.SetInputWeight(1, forwardWeight);
m_Mixer.SetInputWeight(2, backwardsWeight);
m_Mixer.SetInputWeight(3, rightWeight);
m_Mixer.SetInputWeight(4, leftWeight);
m_Mixer.SetInputWeight(5, 0.0f);
m_Mixer.SetInputWeight(6, 0.0f);
}
public IPlayable Build(PlayableGraph graph) {
m_Mixer = AnimationMixerPlayable.Create(graph, 7);
var idlePlayable = AnimationClipPlayable.Create(graph, m_IdleClip);
var runForwardPlayable = AnimationClipPlayable.Create(graph, m_RunForwardClip);
var runBackwardsPlayable = AnimationClipPlayable.Create(graph, m_RunBackwardsClip);
var strafeRightPlayable = AnimationClipPlayable.Create(graph, m_StrafeRightClip);
var strafeLeftPlayable = AnimationClipPlayable.Create(graph, m_StrafeLeftClip);
var turnRightPlayable = AnimationClipPlayable.Create(graph, m_TurnRightClip);
var turnLeftPlayable = AnimationClipPlayable.Create(graph, m_TurnLeftClip);
m_Mixer.ConnectInput(0, idlePlayable, 0, 1.0f);
m_Mixer.ConnectInput(1, runForwardPlayable, 0, 0.0f);
m_Mixer.ConnectInput(2, runBackwardsPlayable, 0, 0.0f);
m_Mixer.ConnectInput(3, strafeRightPlayable, 0, 0.0f);
m_Mixer.ConnectInput(4, strafeLeftPlayable, 0, 0.0f);
m_Mixer.ConnectInput(5, turnRightPlayable, 0, 0.0f);
m_Mixer.ConnectInput(6, turnLeftPlayable, 0, 0.0f);
return m_Mixer;
}
public bool TryFindChild(string name, out IReAnimatorNode node) {
node = null;
return false;
}
public void SetInput(float2 input, float turning) {
m_TargetInput = input;
m_Turning = turning;
}
}
public class PlayerHandsAnimator {
enum State {
None,
@@ -159,11 +45,9 @@ namespace RebootReality.jelycho.Player {
}
switch (m_State) {
case State.QuickAttack: {
if (IsCurrentClipFinished()) {
Debug.Log("QUICK ATTACK FINISHEDDD");
m_AnimancerState.Stop();
// m_AnimancerState.Stop();
m_AnimancerState = null;
OnQuickAttackAnimationFinished?.Invoke();
}
@@ -204,7 +88,7 @@ namespace RebootReality.jelycho.Player {
}
AnimationClip clip = m_ClipsSet.quickAttacks[combo % m_ClipsSet.quickAttacks.Length];
m_AnimancerState = m_AnimancerLayer.Play(clip);
m_AnimancerState = m_AnimancerLayer.Play(clip, 0.1f);
m_State = State.QuickAttack;
}
@@ -214,7 +98,7 @@ namespace RebootReality.jelycho.Player {
return;
}
m_AnimancerState = m_AnimancerLayer.Play(m_ClipsSet.idle);
m_AnimancerState = m_AnimancerLayer.Play(m_ClipsSet.idle, 0.1f);
m_State = State.Idle;
}
@@ -223,7 +107,7 @@ namespace RebootReality.jelycho.Player {
return;
}
m_AnimancerState = m_AnimancerLayer.Play(m_ClipsSet.charging);
m_AnimancerState = m_AnimancerLayer.Play(m_ClipsSet.charging, 0.1f);
m_State = State.Charging;
}
@@ -232,7 +116,7 @@ namespace RebootReality.jelycho.Player {
return;
}
m_AnimancerState = m_AnimancerLayer.Play(m_ClipsSet.chargedIdle);
m_AnimancerState = m_AnimancerLayer.Play(m_ClipsSet.chargedIdle, 0.1f);
m_State = State.ChargedIdle;
}
@@ -241,7 +125,7 @@ namespace RebootReality.jelycho.Player {
return;
}
m_AnimancerState = m_AnimancerLayer.Play(m_ClipsSet.chargedUse);
m_AnimancerState = m_AnimancerLayer.Play(m_ClipsSet.chargedUse, 0.1f);
m_State = State.ChargedUse;
}
@@ -260,36 +144,61 @@ namespace RebootReality.jelycho.Player {
[SerializeField] StringAsset m_GroundLocomotionPropertyY;
SmoothedVector2Parameter m_SmoothLocomotionDirection;
[SerializeField] AvatarMask m_HandsMask;
[SerializeField] TransitionAsset m_GroundTurn;
[SerializeField] StringAsset m_GroundTurnProperty;
SmoothedFloatParameter m_SmoothGroundTurn;
AnimancerLayer m_LocomotionLayer;
[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_LocomotionLayer = m_Animancer.Layers[0];
m_HandsLayer = m_Animancer.Layers[1];
m_LocomotionLayer.SetDebugName("Locomotion");
m_HandsLayer.SetDebugName("Hands");
m_HandsLayer.Mask = m_HandsMask;
m_Hands = new PlayerHandsAnimator(m_HandsLayer);
m_Hands.OnQuickAttackAnimationFinished += () => onQuickAttackFinished.Invoke();
m_Hands.OnCharged += () => onChargeReady.Invoke();
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_LocomotionLayer.Play(m_GroundLocomotion);
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;
}
@@ -301,20 +210,33 @@ namespace RebootReality.jelycho.Player {
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;
}
//m_LegsLayerMixer.SetLayerWeight(1, locomotionParams.IsGrounded ? 0.0f : 1.0f);
var groundBlendDirection = new float2(locomotionParams.VelocityRightNormalized,
locomotionParams.VelocityForwardNormalized);
//m_GroundBlendTree.SetInput(groundBlendDirection, locomotionParams.TurnVelocity);
m_SmoothLocomotionDirection.TargetValue = groundBlendDirection;
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) {
@@ -328,17 +250,9 @@ namespace RebootReality.jelycho.Player {
}
public void PlayQuickAttack(int combo) {
// int index = combo % m_ItemHandsClips.quickAttacks.Length;
// AnimancerState state = m_HandsLayer.Play(m_ItemHandsClips.quickAttacks[index]);
// state.Events(this).OnEnd ??= OnQuickAttackAnimationEnd;
m_Hands.PlayQuickAttack(combo);
}
// void OnQuickAttackAnimationEnd() {
// PlayHandsIdle();
// onQuickAttackFinished?.Invoke();
// }
public void PlayHandsIdle() {
m_Hands.SetIdle();
}
@@ -352,11 +266,12 @@ namespace RebootReality.jelycho.Player {
}
public void PlayKickAnimation() {
// m_LegsLayerMixer.SetLayerWeight(2, 1.0f);
if (m_IsPlayingKickAnimation) {
return;
}
// m_QuickKickNode.PlayOnceWithCallback(() => {
// m_LegsLayerMixer.SetLayerWeight(2, 0.0f);
// });
m_FullBodyLayer.Play(m_KickClip, 0.1f).Time = 0.0f;
m_IsPlayingKickAnimation = true;
}
}
}