animations refactor
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace RebootReality.jelycho.Beacons {
|
||||
public class BeaconGraphics : MonoBehaviour {
|
||||
@@ -9,10 +10,10 @@ namespace RebootReality.jelycho.Beacons {
|
||||
[Range(0.0f, 1.0f)] public float growAmount = 0.5f;
|
||||
[SerializeField] public float growSpeed = 0.5f;
|
||||
|
||||
[SerializeField] public ParticleSystem m_GrowParticleSystem;
|
||||
|
||||
float m_CurrentGrowAmount = 0.0f;
|
||||
|
||||
public UnityEvent onGrowCalled = new UnityEvent();
|
||||
|
||||
void Update() {
|
||||
if (m_CurrentGrowAmount >= 1.0f) {
|
||||
foreach (BeaconElement beaconElement in m_BeaconElements) {
|
||||
@@ -84,7 +85,7 @@ namespace RebootReality.jelycho.Beacons {
|
||||
UpdateElements(m_CurrentGrowAmount);
|
||||
growAmount = 1.0f;
|
||||
|
||||
m_GrowParticleSystem.Play();
|
||||
onGrowCalled?.Invoke();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using RebootKit.Engine.Main;
|
||||
using RebootKit.Engine.Network;
|
||||
using RebootKit.Engine.Simulation;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
using Logger = RebootKit.Engine.Foundation.Logger;
|
||||
|
||||
namespace RebootReality.jelycho.Feedbacks {
|
||||
@@ -12,7 +15,7 @@ namespace RebootReality.jelycho.Feedbacks {
|
||||
public float radius;
|
||||
public float intensity;
|
||||
public float timer;
|
||||
|
||||
|
||||
public static int GetMaxBytes() {
|
||||
return sizeof(float) * 3 + sizeof(float) * 3;
|
||||
}
|
||||
@@ -21,8 +24,33 @@ namespace RebootReality.jelycho.Feedbacks {
|
||||
public class FeedbacksManagerActor : Actor {
|
||||
static readonly Logger s_Logger = new Logger(nameof(FeedbacksManagerActor));
|
||||
|
||||
[SerializeField] Volume m_Volume;
|
||||
|
||||
[SerializeField] float m_QuickAttackIndicatorDuration = 0.3f;
|
||||
[SerializeField] float m_QuickAttackIndicatorPaniniAppearSpeed = 2.0f;
|
||||
[SerializeField] float m_QuickAttackIndicatorChromaticAppearSpeed = 2.0f;
|
||||
[SerializeField] float m_QuickAttackIndicatorPaniniDisappearSpeed = 2.0f;
|
||||
[SerializeField] float m_QuickAttackIndicatorChromaticDisappearSpeed = 2.0f;
|
||||
[SerializeField] float m_QuickAttackIndicatorPaniniIntensity = 0.1f;
|
||||
[SerializeField] float m_QuickAttackIndicatorChromaticIntensity = 1.0f;
|
||||
|
||||
PaniniProjection m_PaniniProjection;
|
||||
ChromaticAberration m_ChromaticAberration;
|
||||
|
||||
float m_QuickAttackTimer;
|
||||
|
||||
List<CameraShakeFeedback> m_ActiveCameraShakes = new List<CameraShakeFeedback>();
|
||||
|
||||
void Awake() {
|
||||
if (!m_Volume.profile.TryGet(out m_PaniniProjection)) {
|
||||
s_Logger.Error($"Failed to find PaniniProjection on volume: {m_Volume.name}");
|
||||
}
|
||||
|
||||
if (!m_Volume.profile.TryGet(out m_ChromaticAberration)) {
|
||||
s_Logger.Error($"Failed to find ChromaticAberration on volume: {m_Volume.name}");
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// @MARK: Camera shake
|
||||
//
|
||||
@@ -31,7 +59,7 @@ namespace RebootReality.jelycho.Feedbacks {
|
||||
s_Logger.Error("ShakeCamera can only be called on the server.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
FeedbacksCameraShakeEvent ev = new FeedbacksCameraShakeEvent {
|
||||
Feedback = new CameraShakeFeedback {
|
||||
center = center,
|
||||
@@ -40,7 +68,7 @@ namespace RebootReality.jelycho.Feedbacks {
|
||||
timer = duration
|
||||
}
|
||||
};
|
||||
SendActorEvent((byte)FeedbacksManagerActorEvents.CameraShake, ref ev);
|
||||
SendActorEvent((byte) FeedbacksManagerActorEvents.CameraShake, ref ev);
|
||||
}
|
||||
|
||||
public float GetShakeIntensityForPosition(Vector3 position) {
|
||||
@@ -70,14 +98,21 @@ namespace RebootReality.jelycho.Feedbacks {
|
||||
|
||||
return intensity;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// @MARK: Local only
|
||||
//
|
||||
public void ShowQuickAttackIndicator() {
|
||||
m_QuickAttackTimer = m_QuickAttackIndicatorDuration;
|
||||
}
|
||||
|
||||
//
|
||||
// @MARK: Actor
|
||||
//
|
||||
protected override IActorData CreateActorData() {
|
||||
return new NoActorData();
|
||||
}
|
||||
|
||||
|
||||
public override void OnClientTick(float deltaTime) {
|
||||
for (int i = m_ActiveCameraShakes.Count - 1; i >= 0; i--) {
|
||||
CameraShakeFeedback feedback = m_ActiveCameraShakes[i];
|
||||
@@ -90,13 +125,40 @@ namespace RebootReality.jelycho.Feedbacks {
|
||||
|
||||
m_ActiveCameraShakes[i] = feedback;
|
||||
}
|
||||
|
||||
m_QuickAttackTimer -= Time.deltaTime;
|
||||
if (m_QuickAttackTimer <= 0.0f) {
|
||||
float chromaticIntensity = m_ChromaticAberration.intensity.value;
|
||||
chromaticIntensity = Mathf.MoveTowards(chromaticIntensity,
|
||||
0.0f,
|
||||
deltaTime * m_QuickAttackIndicatorChromaticDisappearSpeed);
|
||||
m_ChromaticAberration.intensity.value = chromaticIntensity;
|
||||
|
||||
float paniniIntensity = m_PaniniProjection.distance.value;
|
||||
paniniIntensity = Mathf.MoveTowards(paniniIntensity,
|
||||
0.0f,
|
||||
deltaTime * m_QuickAttackIndicatorPaniniDisappearSpeed);
|
||||
m_PaniniProjection.distance.value = paniniIntensity;
|
||||
} else {
|
||||
float chromaticIntensity = m_ChromaticAberration.intensity.value;
|
||||
chromaticIntensity = Mathf.MoveTowards(chromaticIntensity,
|
||||
m_QuickAttackIndicatorChromaticIntensity,
|
||||
deltaTime * m_QuickAttackIndicatorChromaticAppearSpeed);
|
||||
m_ChromaticAberration.intensity.value = chromaticIntensity;
|
||||
|
||||
float paniniIntensity = m_PaniniProjection.distance.value;
|
||||
paniniIntensity = Mathf.MoveTowards(paniniIntensity,
|
||||
m_QuickAttackIndicatorPaniniIntensity,
|
||||
deltaTime * m_QuickAttackIndicatorPaniniAppearSpeed);
|
||||
m_PaniniProjection.distance.value = paniniIntensity;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnActorEventClient(ActorEvent actorEvent) {
|
||||
FeedbacksManagerActorEvents feedbackEvent = (FeedbacksManagerActorEvents)actorEvent.EventID;
|
||||
FeedbacksManagerActorEvents feedbackEvent = (FeedbacksManagerActorEvents) actorEvent.EventID;
|
||||
|
||||
switch (feedbackEvent) {
|
||||
|
||||
|
||||
case FeedbacksManagerActorEvents.CameraShake: {
|
||||
FeedbacksCameraShakeEvent ev = new FeedbacksCameraShakeEvent();
|
||||
DataSerializationUtils.Deserialize(actorEvent.Data, ref ev);
|
||||
@@ -106,11 +168,11 @@ namespace RebootReality.jelycho.Feedbacks {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum FeedbacksManagerActorEvents : byte {
|
||||
None = 0x00,
|
||||
CameraShake = 0x01,
|
||||
|
||||
@@ -15,19 +15,25 @@ namespace RebootReality.jelycho.Items {
|
||||
void Attack(Actor attacker, ItemActor itemActor);
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ItemHandsAnimationClipsSet {
|
||||
public AnimationClip idle;
|
||||
public AnimationClip charging;
|
||||
public AnimationClip chargedIdle;
|
||||
public AnimationClip chargedUse;
|
||||
|
||||
public AnimationClip[] quickAttacks;
|
||||
public AnimationClip block;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ItemConfig {
|
||||
public Sprite icon;
|
||||
|
||||
[MaxLength(32)] public string characterEquippedMountSlotName = "hand_right";
|
||||
|
||||
[Header("Character Animations Names")]
|
||||
public string idleAnimation;
|
||||
public string chargingAnimation;
|
||||
public string chargedUseAnimation;
|
||||
|
||||
public string[] quickAttacksAnimations;
|
||||
public string blockAnimation;
|
||||
[Header("Character Animations")]
|
||||
public ItemHandsAnimationClipsSet handsAnimationClipsSets;
|
||||
|
||||
[Header("Quick Attack")]
|
||||
public bool canQuickAttack = false;
|
||||
|
||||
11
Assets/jelycho/Code/Player/FootstepSfxPlayer.cs
Normal file
11
Assets/jelycho/Code/Player/FootstepSfxPlayer.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace RebootReality.jelycho.Player {
|
||||
public class FootstepSfxPlayer : MonoBehaviour {
|
||||
[SerializeField] AudioSource m_AudioSource;
|
||||
|
||||
public void PlayFootstep() {
|
||||
m_AudioSource.Play();
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/jelycho/Code/Player/FootstepSfxPlayer.cs.meta
Normal file
3
Assets/jelycho/Code/Player/FootstepSfxPlayer.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd9bb0d58ecf466d8fe6d4cd19b51e5c
|
||||
timeCreated: 1754291034
|
||||
@@ -16,7 +16,7 @@ namespace RebootReality.jelycho.Player {
|
||||
public class PlayerActor : Actor {
|
||||
static readonly Logger s_Logger = new Logger(nameof(PlayerActor));
|
||||
|
||||
[SerializeField] Animator m_Animator;
|
||||
[SerializeField] PlayerAnimator m_PlayerAnimator;
|
||||
|
||||
[Header("Movement")]
|
||||
[SerializeField] PlayerFPPLocomotion m_Locomotion;
|
||||
@@ -84,6 +84,16 @@ namespace RebootReality.jelycho.Player {
|
||||
bool m_IsCharging;
|
||||
float m_ChargeTimer;
|
||||
|
||||
[SerializeField] float m_QuickAttackComboMaxDelay = 0.5f;
|
||||
|
||||
enum QuickAttackState {
|
||||
None,
|
||||
PlayingAnimation,
|
||||
WaitingForNextAttack
|
||||
}
|
||||
|
||||
QuickAttackState m_QuickAttackState;
|
||||
|
||||
int m_QuickAttackComboCounter;
|
||||
float m_QuickAttackComboTimer;
|
||||
|
||||
@@ -122,11 +132,15 @@ namespace RebootReality.jelycho.Player {
|
||||
void OnEnable() {
|
||||
Inventory.OnItemPickedUp += OnItemPickedUp;
|
||||
Inventory.OnItemDropped += OnItemDropped;
|
||||
|
||||
m_PlayerAnimator.onQuickAttackFinished.AddListener(OnQuickAttackFinishedAnimation);
|
||||
}
|
||||
|
||||
void OnDisable() {
|
||||
Inventory.OnItemPickedUp -= OnItemPickedUp;
|
||||
Inventory.OnItemDropped -= OnItemDropped;
|
||||
|
||||
m_PlayerAnimator.onQuickAttackFinished.RemoveListener(OnQuickAttackFinishedAnimation);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -193,10 +207,6 @@ namespace RebootReality.jelycho.Player {
|
||||
|
||||
m_IsCharging = false;
|
||||
m_ChargeTimer = 0.0f;
|
||||
|
||||
if (m_QuickAttackComboTimer <= 0.0f) {
|
||||
m_QuickAttackComboCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void HoldingPrimaryAction() {
|
||||
@@ -264,12 +274,35 @@ namespace RebootReality.jelycho.Player {
|
||||
|
||||
m_IsCharging = false;
|
||||
} else if (m_EquippedItem.Config.canQuickAttack) {
|
||||
PlayQuickAttackAnimation(m_QuickAttackComboCounter);
|
||||
m_QuickAttackComboCounter += 1;
|
||||
m_QuickAttackComboTimer = 2.0f;
|
||||
if (m_QuickAttackState == QuickAttackState.None) {
|
||||
m_QuickAttackComboCounter = 0;
|
||||
PlayQuickAttackAnimation(m_QuickAttackComboCounter);
|
||||
m_QuickAttackState = QuickAttackState.PlayingAnimation;
|
||||
} else if (m_QuickAttackState == QuickAttackState.PlayingAnimation) {
|
||||
m_QuickAttackComboCounter = 0;
|
||||
} else if (m_QuickAttackState == QuickAttackState.WaitingForNextAttack) {
|
||||
m_QuickAttackComboCounter += 1;
|
||||
PlayQuickAttackAnimation(m_QuickAttackComboCounter);
|
||||
m_QuickAttackState = QuickAttackState.PlayingAnimation;
|
||||
|
||||
if (m_EquippedItem.Config.quickAttackAction != null) {
|
||||
m_EquippedItem.Config.quickAttackAction.Attack(this, m_EquippedItem);
|
||||
if (m_EquippedItem.Config.quickAttackAction != null) {
|
||||
m_EquippedItem.Config.quickAttackAction.Attack(this, m_EquippedItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnQuickAttackFinishedAnimation() {
|
||||
if (m_QuickAttackState != QuickAttackState.PlayingAnimation) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_QuickAttackComboTimer = m_QuickAttackComboMaxDelay;
|
||||
m_QuickAttackState = QuickAttackState.WaitingForNextAttack;
|
||||
|
||||
if (m_IsSetupAsOwner) {
|
||||
if (RR.World.Context is WorldContext context) {
|
||||
context.FeedbacksManager.ShowQuickAttackIndicator();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -306,60 +339,51 @@ namespace RebootReality.jelycho.Player {
|
||||
// @MARK: Hands animations
|
||||
//
|
||||
void PlayHandsAnimation(string animationName) {
|
||||
int hash = Animator.StringToHash(animationName);
|
||||
|
||||
if (!m_Animator.HasState(m_HandsLayerIndex, hash)) {
|
||||
s_Logger.Error($"Animator does not have state with name {animationName}");
|
||||
return;
|
||||
}
|
||||
|
||||
PlayHandsAnimation(hash);
|
||||
// int hash = Animator.StringToHash(animationName);
|
||||
//
|
||||
// if (!m_Animator.HasState(m_HandsLayerIndex, hash)) {
|
||||
// s_Logger.Error($"Animator does not have state with name {animationName}");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// PlayHandsAnimation(hash);
|
||||
}
|
||||
|
||||
void PlayHandsAnimation(int animationHash) {
|
||||
m_Animator.CrossFade(animationHash, 0.0f, m_HandsLayerIndex);
|
||||
|
||||
if (RR.IsServer()) {
|
||||
PlayerPlayHandsAnimationEvent handsAnimationEvent = new PlayerPlayHandsAnimationEvent {
|
||||
AnimationHash = animationHash
|
||||
};
|
||||
SendActorEvent((byte)PlayerActorEvents.PlayHandsAnimation, ref handsAnimationEvent);
|
||||
} else {
|
||||
PlayerActorRequestHandsAnimationCommand handsAnimationCommand =
|
||||
new PlayerActorRequestHandsAnimationCommand {
|
||||
AnimationHash = animationHash
|
||||
};
|
||||
SendActorCommand((byte) PlayerActorCommands.RequestHandsAnimation, ref handsAnimationCommand);
|
||||
}
|
||||
// m_Animator.CrossFade(animationHash, 0.0f, m_HandsLayerIndex);
|
||||
//
|
||||
// if (RR.IsServer()) {
|
||||
// PlayerPlayHandsAnimationEvent handsAnimationEvent = new PlayerPlayHandsAnimationEvent {
|
||||
// AnimationHash = animationHash
|
||||
// };
|
||||
// SendActorEvent((byte)PlayerActorEvents.PlayHandsAnimation, ref handsAnimationEvent);
|
||||
// } else {
|
||||
// PlayerActorRequestHandsAnimationCommand handsAnimationCommand =
|
||||
// new PlayerActorRequestHandsAnimationCommand {
|
||||
// AnimationHash = animationHash
|
||||
// };
|
||||
// SendActorCommand((byte) PlayerActorCommands.RequestHandsAnimation, ref handsAnimationCommand);
|
||||
// }
|
||||
}
|
||||
|
||||
void SetHandsIdleAnimation() {
|
||||
if (m_EquippedItem != null) {
|
||||
PlayHandsAnimation(m_EquippedItem.Config.idleAnimation);
|
||||
} else {
|
||||
PlayHandsAnimation(m_HandsIdleStateName);
|
||||
}
|
||||
m_PlayerAnimator.PlayHandsIdle();
|
||||
}
|
||||
|
||||
void SetChargingAnimation() {
|
||||
if (m_EquippedItem != null) {
|
||||
PlayHandsAnimation(m_EquippedItem.Config.chargingAnimation);
|
||||
// PlayHandsAnimation(m_EquippedItem.Config.chargingAnimation);
|
||||
}
|
||||
}
|
||||
|
||||
void SetChargedUseAnimation() {
|
||||
if (m_EquippedItem != null) {
|
||||
PlayHandsAnimation(m_EquippedItem.Config.chargedUseAnimation);
|
||||
// PlayHandsAnimation(m_EquippedItem.Config.chargedUseAnimation);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayQuickAttackAnimation(int combo) {
|
||||
if (m_EquippedItem == null || m_EquippedItem.Config.quickAttacksAnimations.Length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
string animationName = m_EquippedItem.Config.quickAttacksAnimations[combo % m_EquippedItem.Config.quickAttacksAnimations.Length];
|
||||
PlayHandsAnimation(animationName);
|
||||
m_PlayerAnimator.PlayQuickAttack(combo);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -382,6 +406,11 @@ namespace RebootReality.jelycho.Player {
|
||||
|
||||
if (m_QuickAttackComboTimer > 0.0f) {
|
||||
m_QuickAttackComboTimer -= deltaTime;
|
||||
|
||||
if (m_QuickAttackComboTimer <= 0.0f && m_QuickAttackState == QuickAttackState.WaitingForNextAttack) {
|
||||
m_QuickAttackState = QuickAttackState.None;
|
||||
SetHandsIdleAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
m_SyncRemoteStateTimer -= deltaTime;
|
||||
@@ -465,14 +494,14 @@ namespace RebootReality.jelycho.Player {
|
||||
PlayerActorRequestHandsAnimationCommand command = new PlayerActorRequestHandsAnimationCommand();
|
||||
DataSerializationUtils.Deserialize(actorCommand.Data, ref command);
|
||||
|
||||
if (m_Animator.HasState(m_HandsLayerIndex, command.AnimationHash)) {
|
||||
PlayerPlayHandsAnimationEvent handsAnimationEvent = new PlayerPlayHandsAnimationEvent {
|
||||
AnimationHash = command.AnimationHash
|
||||
};
|
||||
SendActorEvent((byte)PlayerActorEvents.PlayHandsAnimation, ref handsAnimationEvent);
|
||||
} else {
|
||||
s_Logger.Error($"Animator does not have state with hash {command.AnimationHash}");
|
||||
}
|
||||
// if (m_Animator.HasState(m_HandsLayerIndex, command.AnimationHash)) {
|
||||
// PlayerPlayHandsAnimationEvent handsAnimationEvent = new PlayerPlayHandsAnimationEvent {
|
||||
// AnimationHash = command.AnimationHash
|
||||
// };
|
||||
// SendActorEvent((byte)PlayerActorEvents.PlayHandsAnimation, ref handsAnimationEvent);
|
||||
// } else {
|
||||
// s_Logger.Error($"Animator does not have state with hash {command.AnimationHash}");
|
||||
// }
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -561,11 +590,11 @@ namespace RebootReality.jelycho.Player {
|
||||
PlayerPlayHandsAnimationEvent handsAnimationEvent = new PlayerPlayHandsAnimationEvent();
|
||||
DataSerializationUtils.Deserialize(actorEvent.Data, ref handsAnimationEvent);
|
||||
|
||||
if (m_Animator.HasState(m_HandsLayerIndex, handsAnimationEvent.AnimationHash)) {
|
||||
m_Animator.CrossFade(handsAnimationEvent.AnimationHash, 0.0f, m_HandsLayerIndex);
|
||||
} else {
|
||||
s_Logger.Error($"Animator does not have state with hash {handsAnimationEvent.AnimationHash}");
|
||||
}
|
||||
// if (m_Animator.HasState(m_HandsLayerIndex, handsAnimationEvent.AnimationHash)) {
|
||||
// m_Animator.CrossFade(handsAnimationEvent.AnimationHash, 0.0f, m_HandsLayerIndex);
|
||||
// } else {
|
||||
// s_Logger.Error($"Animator does not have state with hash {handsAnimationEvent.AnimationHash}");
|
||||
// }
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -690,8 +719,6 @@ namespace RebootReality.jelycho.Player {
|
||||
return;
|
||||
}
|
||||
|
||||
// @BUG: Sometimes the item will not update it's physics state and will keep floating in the air. It's rare?
|
||||
|
||||
UpdateEquippedItem();
|
||||
|
||||
item.SetHidden(false);
|
||||
@@ -740,6 +767,12 @@ namespace RebootReality.jelycho.Player {
|
||||
};
|
||||
SendActorEvent((byte) PlayerActorEvents.PrimaryEquippedItemChanged, ref itemChangedEvent);
|
||||
|
||||
if (m_EquippedItem != null) {
|
||||
m_PlayerAnimator.SetHandsAnimationSet(m_EquippedItem.Config.handsAnimationClipsSets);
|
||||
} else {
|
||||
m_PlayerAnimator.SetHandsAnimationSet(null);
|
||||
}
|
||||
|
||||
SetHandsIdleAnimation();
|
||||
}
|
||||
|
||||
@@ -756,12 +789,13 @@ namespace RebootReality.jelycho.Player {
|
||||
// @MARK: Common
|
||||
//
|
||||
void TickCharacterRotation() {
|
||||
// @TODO: restore old delayed character rotation
|
||||
m_Locomotion.YawRotation = m_Camera.Yaw;
|
||||
|
||||
float3 targetCharacterForward = math.normalize(LookDirection.With(y: 0.0f));
|
||||
float3 currentCharacterForward = math.normalize(m_CharacterForwardTransform.forward.With(y: 0.0f));
|
||||
|
||||
float angleRad =
|
||||
math.acos(math.clamp(math.dot(targetCharacterForward, currentCharacterForward) / (math.length(targetCharacterForward) * math.length(currentCharacterForward)),
|
||||
-1f, 1f));
|
||||
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;
|
||||
@@ -886,14 +920,6 @@ namespace RebootReality.jelycho.Player {
|
||||
//
|
||||
// @MARK: Animations
|
||||
//
|
||||
|
||||
struct AnimatorParamHashes {
|
||||
public static readonly int VelocityForwardNormalized = Animator.StringToHash("VelocityForwardNormalized");
|
||||
public static readonly int VelocityRightNormalized = Animator.StringToHash("VelocityRightNormalized");
|
||||
public static readonly int TurnVelocity = Animator.StringToHash("TurnVelocity");
|
||||
public static readonly int IsGrounded = Animator.StringToHash("IsGrounded");
|
||||
}
|
||||
|
||||
void UpdateAnimator(Vector3 velocity) {
|
||||
Vector3 localVelocity = m_CharacterForwardTransform.InverseTransformDirection(velocity);
|
||||
float forwardNormalized = localVelocity.z / m_Locomotion.runSpeed;
|
||||
@@ -906,11 +932,13 @@ namespace RebootReality.jelycho.Player {
|
||||
turnVelocity = 0.0f;
|
||||
}
|
||||
|
||||
m_Animator.SetFloat(AnimatorParamHashes.VelocityForwardNormalized, forwardNormalized);
|
||||
m_Animator.SetFloat(AnimatorParamHashes.VelocityRightNormalized, rightNormalized);
|
||||
m_Animator.SetFloat(AnimatorParamHashes.TurnVelocity, turnVelocity);
|
||||
|
||||
m_Animator.SetBool(AnimatorParamHashes.IsGrounded, m_Locomotion.IsGrounded);
|
||||
PlayerLocomotionAnimatorParams locomotionParams = new PlayerLocomotionAnimatorParams {
|
||||
IsGrounded = m_Locomotion.IsGrounded,
|
||||
VelocityForwardNormalized = forwardNormalized,
|
||||
VelocityRightNormalized = rightNormalized,
|
||||
TurnVelocity = turnVelocity
|
||||
};
|
||||
m_PlayerAnimator.SetLocomotionParams(locomotionParams);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
166
Assets/jelycho/Code/Player/PlayerAnimator.cs
Normal file
166
Assets/jelycho/Code/Player/PlayerAnimator.cs
Normal file
@@ -0,0 +1,166 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using RebootKit.Engine.Animations;
|
||||
using RebootReality.jelycho.Items;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Animations;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.Playables;
|
||||
|
||||
namespace RebootReality.jelycho.Player {
|
||||
public struct PlayerLocomotionAnimatorParams {
|
||||
public float VelocityForwardNormalized;
|
||||
public float VelocityRightNormalized;
|
||||
public float TurnVelocity;
|
||||
public bool IsGrounded;
|
||||
}
|
||||
|
||||
public class CharacterHandsReAnimatorNode : IReAnimatorNode {
|
||||
enum State {
|
||||
None,
|
||||
Idle,
|
||||
QuickAttack
|
||||
}
|
||||
|
||||
[field: SerializeField] public string Name { get; private set; }
|
||||
|
||||
PlayableGraph m_Graph;
|
||||
AnimationMixerPlayable m_Mixer;
|
||||
AnimationClipPlayable m_CurrentPlayable;
|
||||
|
||||
ItemHandsAnimationClipsSet m_ClipsSet;
|
||||
|
||||
State m_State;
|
||||
|
||||
public event Action OnQuickAttackAnimationFinished = delegate { };
|
||||
|
||||
public void Tick(float deltaTime) {
|
||||
switch (m_State) {
|
||||
|
||||
case State.QuickAttack: {
|
||||
if (m_CurrentPlayable.GetTime() >= m_CurrentPlayable.GetAnimationClip().length &&
|
||||
m_CurrentPlayable.GetPlayState() == PlayState.Playing) {
|
||||
m_CurrentPlayable.Pause();
|
||||
OnQuickAttackAnimationFinished?.Invoke();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public IPlayable Build(PlayableGraph graph) {
|
||||
m_Graph = graph;
|
||||
m_Mixer = AnimationMixerPlayable.Create(graph, 1);
|
||||
return m_Mixer;
|
||||
}
|
||||
|
||||
public bool TryFindChild(string name, out IReAnimatorNode node) {
|
||||
node = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
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_CurrentPlayable = AnimationClipPlayable.Create(m_Graph, clip);
|
||||
|
||||
m_Mixer.DisconnectInput(0);
|
||||
m_Mixer.ConnectInput(0, m_CurrentPlayable, 0, 1.0f);
|
||||
|
||||
m_State = State.QuickAttack;
|
||||
m_CurrentPlayable.Play();
|
||||
}
|
||||
|
||||
public void SetIdle() {
|
||||
if (m_ClipsSet == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_CurrentPlayable = AnimationClipPlayable.Create(m_Graph, m_ClipsSet.idle);
|
||||
m_Mixer.DisconnectInput(0);
|
||||
m_Mixer.ConnectInput(0, m_CurrentPlayable, 0, 1.0f);
|
||||
|
||||
m_CurrentPlayable.Play();
|
||||
m_State = State.Idle;
|
||||
}
|
||||
}
|
||||
|
||||
public class PlayerAnimator : MonoBehaviour {
|
||||
[SerializeField] ReAnimator m_ReAnimator;
|
||||
|
||||
MixerNode m_LocomotionRootMixer;
|
||||
BlendTree2DNode m_GroundBlendTree;
|
||||
CharacterHandsReAnimatorNode m_Hands;
|
||||
|
||||
// @TODO: for some reason `SetLocomotionParams` is called before awake
|
||||
bool m_IsReady = false;
|
||||
|
||||
public UnityEvent onQuickAttackFinished = new UnityEvent();
|
||||
|
||||
void Awake() {
|
||||
m_LocomotionRootMixer = m_ReAnimator.FindNode<MixerNode>("locomotion_root");
|
||||
m_LocomotionRootMixer.SetInputWeight(0, 1.0f);
|
||||
m_LocomotionRootMixer.SetInputWeight(1, 0.0f);
|
||||
|
||||
m_GroundBlendTree = m_ReAnimator.FindNode<BlendTree2DNode>("locomotion_ground");
|
||||
m_GroundBlendTree.SetDirection(new float2(0, 1));
|
||||
|
||||
m_Hands = m_ReAnimator.FindNode<CharacterHandsReAnimatorNode>("hands");
|
||||
m_Hands.OnQuickAttackAnimationFinished += () => { onQuickAttackFinished?.Invoke(); };
|
||||
|
||||
m_IsReady = true;
|
||||
}
|
||||
|
||||
void Update() {
|
||||
|
||||
}
|
||||
|
||||
public void SetLocomotionParams(PlayerLocomotionAnimatorParams locomotionParams) {
|
||||
if (!m_IsReady) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_LocomotionRootMixer.SetInputWeight(0, locomotionParams.IsGrounded ? 1.0f : 0.0f);
|
||||
m_LocomotionRootMixer.SetInputWeight(1, locomotionParams.IsGrounded ? 0.0f : 1.0f);
|
||||
|
||||
float2 groundBlendDirection = new float2(locomotionParams.VelocityRightNormalized,
|
||||
locomotionParams.VelocityForwardNormalized);
|
||||
m_GroundBlendTree.SetDirection(groundBlendDirection);
|
||||
}
|
||||
|
||||
public void SetHandsAnimationSet(ItemHandsAnimationClipsSet clipsSet) {
|
||||
if (clipsSet == null) {
|
||||
m_ReAnimator.SetLayerWeight(1, 0.0f);
|
||||
return;
|
||||
}
|
||||
|
||||
m_ReAnimator.SetLayerWeight(1, 1.0f);
|
||||
m_Hands.UpdateClips(clipsSet);
|
||||
}
|
||||
|
||||
public void PlayQuickAttack(int combo) {
|
||||
m_Hands.PlayQuickAttack(combo);
|
||||
}
|
||||
|
||||
public void PlayHandsIdle() {
|
||||
m_Hands.SetIdle();
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/jelycho/Code/Player/PlayerAnimator.cs.meta
Normal file
3
Assets/jelycho/Code/Player/PlayerAnimator.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 00a9f9c2ce4a41fdaadcfb24c4233127
|
||||
timeCreated: 1754007861
|
||||
Reference in New Issue
Block a user