namespace TPSBR { using System; using UnityEngine; using UnityEngine.Animations; using UnityEngine.Playables; using Fusion.Addons.KCC; using Fusion.Addons.AnimationController; using AnimationState = Fusion.Addons.AnimationController.AnimationState; public sealed unsafe class TurnState : AnimationState { // PUBLIC MEMBERS public float BlendSpeed => _blendSpeed; public float TurnSpeed => _turnSpeed; [NonSerialized][AnimationProperty(nameof(InterpolateAnimationTime))] public float AnimationTime; [NonSerialized][AnimationProperty(nameof(InterpolateRemainingTime))] public float RemainingTime; [NonSerialized] public float InterpolatedAnimationTime; [NonSerialized] public float InterpolatedRemainingTime; // PRIVATE MEMBERS [SerializeField] private ClipNode[] _nodes; [SerializeField] private float _blendSpeed = 1.0f; [SerializeField] private float _turnSpeed = 1.0f; [SerializeField] private float _animationPower = 1.0f; [SerializeField] private float _maxAnimationSpeed = 1.0f; private KCC _kcc; private Agent _agent; private Weapons _weapons; private AnimationMixerPlayable _mixer; // PUBLIC METHODS public void Refresh(float angle) { if (angle < 0.0f && RemainingTime <= 0.0f) { RemainingTime = Mathf.Clamp(RemainingTime + angle * _turnSpeed, -_maxAnimationSpeed, 0.0f); } if (angle > 0.0f && RemainingTime >= 0.0f) { RemainingTime = Mathf.Clamp(RemainingTime + angle * _turnSpeed, 0.0f, _maxAnimationSpeed); } } // AnimationState INTERFACE protected override void CreatePlayable() { _mixer = AnimationMixerPlayable.Create(Controller.Graph, _nodes.Length); for (int i = 0, count = _nodes.Length; i < count; ++i) { ClipNode node = _nodes[i]; node.CreatePlayable(Controller.Graph); _mixer.ConnectInput(i, node.PlayableClip, 0); } AddPlayable(_mixer, 0); } protected override void OnInitialize() { base.OnInitialize(); _kcc = Controller.GetComponentNoAlloc(); _agent = Controller.GetComponentNoAlloc(); _weapons = Controller.GetComponentNoAlloc(); } protected override void OnDespawned() { if (_mixer.IsValid() == true) { _mixer.Destroy(); } for (int i = 0, count = _nodes.Length; i < count; ++i) { _nodes[i].DestroyPlayable(); } } protected override void OnFixedUpdate() { int idleClipID = GetClipID(); int turnClipID; float remainingTime = Mathf.Abs(RemainingTime); float remainingDeltaTime = Controller.DeltaTime * Mathf.Max(0.5f, remainingTime); float animationDeltaTime = Controller.DeltaTime * remainingTime; if (RemainingTime <= 0.0f) { turnClipID = idleClipID + 1; RemainingTime = Mathf.Clamp(RemainingTime + remainingDeltaTime * _blendSpeed, -_maxAnimationSpeed, 0.0f); } else { turnClipID = idleClipID + 2; RemainingTime = Mathf.Clamp(RemainingTime - remainingDeltaTime * _blendSpeed, 0.0f, _maxAnimationSpeed); } for (int i = 0, count = _nodes.Length; i < count; ++i) { _mixer.SetInputWeight(i, 0.0f); } ClipNode idleNode = _nodes[idleClipID]; ClipNode turnNode = _nodes[turnClipID]; float animationTime = AnimationTime + animationDeltaTime * turnNode.Speed / turnNode.Length; if (animationTime >= 1.0f) { animationTime %= 1.0f; } AnimationTime = animationTime; _mixer.SetInputWeight(idleClipID, 1.0f - _animationPower); _mixer.SetInputWeight(turnClipID, _animationPower); idleNode.PlayableClip.SetTime(animationTime * idleNode.Length); turnNode.PlayableClip.SetTime(animationTime * turnNode.Length); } protected override void OnInterpolate() { int idleClipID = GetClipID(); int turnClipID = InterpolatedRemainingTime <= 0.0f ? (idleClipID + 1) : (idleClipID + 2); for (int i = 0, count = _nodes.Length; i < count; ++i) { _mixer.SetInputWeight(i, 0.0f); } _mixer.SetInputWeight(idleClipID, 1.0f - _animationPower); _mixer.SetInputWeight(turnClipID, _animationPower); ClipNode idleNode = _nodes[idleClipID]; idleNode.PlayableClip.SetTime(InterpolatedAnimationTime * idleNode.Length); ClipNode turnNode = _nodes[turnClipID]; turnNode.PlayableClip.SetTime(InterpolatedAnimationTime * turnNode.Length); } protected override void OnDeactivate() { RemainingTime = 0.0f; } protected override void OnSetDefaults() { AnimationTime = 0.0f; } // PRIVATE METHODS private int GetClipID() { int currentWeaponSlot = _weapons.CurrentWeaponSlot; if (currentWeaponSlot > 2) { currentWeaponSlot = 1; // For grenades we use pistol set } if (currentWeaponSlot < 0) return 0; return currentWeaponSlot * 3; } private void InterpolateAnimationTime(AnimationInterpolationInfo interpolationInfo) { InterpolatedAnimationTime = AnimationUtility.InterpolateTime(interpolationInfo.FromBuffer.ReinterpretState(interpolationInfo.Offset), interpolationInfo.ToBuffer.ReinterpretState(interpolationInfo.Offset), 1.0f, interpolationInfo.Alpha); } private void InterpolateRemainingTime(AnimationInterpolationInfo interpolationInfo) { InterpolatedRemainingTime = Mathf.LerpUnclamped(interpolationInfo.FromBuffer.ReinterpretState(interpolationInfo.Offset), interpolationInfo.ToBuffer.ReinterpretState(interpolationInfo.Offset), interpolationInfo.Alpha); } } }