282 lines
8.8 KiB
C#
Raw Normal View History

2025-09-24 11:24:38 +05:00
namespace TPSBR
{
using UnityEngine;
using Fusion.Addons.KCC;
using Fusion.Addons.AnimationController;
public sealed class MoveState : MultiBlendTreeState
{
// PUBLIC MEMBERS
public Vector3 FixedDirection => _fixedDirection;
public float FixedMagnitude => _fixedMagnitude;
public Vector3 InterpolatedDirection => _interpolatedDirection;
public float InterpolatedMagnitude => _interpolatedMagnitude;
// PRIVATE MEMBERS
[SerializeField]
private float _minAnimationSpeed = 0.25f;
[SerializeField]
private float _maxAnimationSpeed = 2.0f;
[SerializeField]
private float _directionSmoothingSpeed = 16.0f;
[SerializeField]
private float _magnitudeSmoothingSpeed = 16.0f;
private KCC _kcc;
private Agent _agent;
private Weapons _weapons;
private Vector3 _fixedDirection;
private float _fixedMagnitude;
private Vector3 _interpolatedDirection;
private float _interpolatedMagnitude;
// PUBLIC METHODS
public float GetBaseSpeed(Vector2 localNormalizedDirection, float multiplier)
{
if (multiplier == default)
{
multiplier = GetMultiplier();
}
return GetMaxBaseSpeed(localNormalizedDirection) * multiplier;
}
// MultiBlendTreeState INTERFACE
public override Vector2 GetBlendPosition(bool interpolated)
{
Vector3 direction = interpolated == true ? _interpolatedDirection : _fixedDirection;
float magnitude = interpolated == true ? _interpolatedMagnitude : _fixedMagnitude;
Vector3 blendPosition = _kcc.transform.InverseTransformDirection(direction).XZ0().normalized * magnitude;
if (_agent != null && _agent.Runner != null && _agent.LeftSide == true)
{
blendPosition.x = -blendPosition.x;
}
return blendPosition;
}
public override float GetSpeedMultiplier()
{
KCCData kccData = _kcc.Object.IsInSimulation == true ? _kcc.FixedData : _kcc.Data;
float maxBaseSpeed = GetMaxBaseSpeed((Quaternion.Inverse(kccData.TransformRotation) * kccData.KinematicDirection).XZ0().normalized);
if (maxBaseSpeed > 0.0f && kccData.RealSpeed > maxBaseSpeed)
return Mathf.Clamp(kccData.RealSpeed / maxBaseSpeed, _minAnimationSpeed, _maxAnimationSpeed);
return 1.0f;
}
public override int GetSetID()
{
int currentWeaponSlot = _weapons.CurrentWeaponSlot;
if (currentWeaponSlot > 2)
{
currentWeaponSlot = 1; // For grenades we use pistol set
}
if (currentWeaponSlot < 0)
return 0;
return currentWeaponSlot;
}
// AnimationState INTERFACE
protected override void OnInitialize()
{
base.OnInitialize();
_kcc = Controller.GetComponentNoAlloc<KCC>();
_agent = Controller.GetComponentNoAlloc<Agent>();
_weapons = Controller.GetComponentNoAlloc<Weapons>();
}
protected override void OnSpawned()
{
base.OnSpawned();
_fixedDirection = Vector3.forward;
_fixedMagnitude = 0.0f;
_interpolatedDirection = Vector3.forward;
_interpolatedMagnitude = 0.0f;
}
protected override void OnFixedUpdate()
{
SetFixedProperties();
base.OnFixedUpdate();
}
protected override void OnInterpolate()
{
SetInterpolatedProperties();
base.OnInterpolate();
}
// PRIVATE METHODS
private void SetFixedProperties()
{
KCCData kccFixedData = _kcc.FixedData;
Vector3 targetDirection = _fixedDirection;
float targetMagnitude;
if (Controller.HasInputAuthority == true || Controller.HasStateAuthority == true)
{
if (kccFixedData.InputDirection.OnlyXZ().IsAlmostZero(0.025f) == false)
{
targetDirection = kccFixedData.InputDirection.OnlyXZ().normalized;
}
else if (kccFixedData.KinematicDirection.OnlyXZ().IsAlmostZero(0.025f) == false)
{
targetDirection = kccFixedData.KinematicDirection.OnlyXZ().normalized;
}
else if (kccFixedData.DesiredVelocity.OnlyXZ().IsAlmostZero(0.025f) == false)
{
targetDirection = kccFixedData.DesiredVelocity.OnlyXZ().normalized;
}
float realVelocityMagnitude = kccFixedData.RealVelocity.OnlyXZ().magnitude;
float desiredVelocityMagnitude = kccFixedData.DesiredVelocity.OnlyXZ().magnitude;
float kinematicVelocityMagnitude = kccFixedData.KinematicVelocity.OnlyXZ().magnitude;
targetMagnitude = Mathf.Min(realVelocityMagnitude, Mathf.Max(kinematicVelocityMagnitude, desiredVelocityMagnitude));
}
else
{
if (kccFixedData.RealVelocity.OnlyXZ().IsAlmostZero(0.025f) == false)
{
targetDirection = kccFixedData.RealVelocity.OnlyXZ().normalized;
}
targetMagnitude = kccFixedData.RealSpeed;
}
_fixedDirection = targetDirection;
_fixedMagnitude = targetMagnitude;
}
private void SetInterpolatedProperties()
{
KCCData kccFixedData = _kcc.FixedData;
KCCData kccRenderData = _kcc.RenderData;
Vector3 targetDirection = _interpolatedDirection;
float targetMagnitude;
if (Controller.HasInputAuthority == true || Controller.HasStateAuthority == true)
{
if (kccRenderData.InputDirection.OnlyXZ().IsAlmostZero(0.025f) == false)
{
targetDirection = kccRenderData.InputDirection.OnlyXZ().normalized;
}
else if (kccRenderData.KinematicDirection.OnlyXZ().IsAlmostZero(0.025f) == false)
{
targetDirection = kccRenderData.KinematicDirection.OnlyXZ().normalized;
}
else if (kccFixedData.DesiredVelocity.OnlyXZ().IsAlmostZero(0.025f) == false)
{
targetDirection = kccFixedData.DesiredVelocity.OnlyXZ().normalized;
}
float directionDot = Vector3.Dot(targetDirection, _interpolatedDirection);
if (directionDot < -0.5f)
{
const float angleTolerance = 25.0f;
float angle = Vector3.SignedAngle(_kcc.transform.forward, _interpolatedDirection, Vector3.up);
if (angle.AlmostEquals(90.0f, angleTolerance) == true)
{
targetDirection = Quaternion.Euler(0.0f, -90.0f, 0.0f) * _interpolatedDirection;
}
else if (angle.AlmostEquals(-90.0f, angleTolerance) == true)
{
targetDirection = Quaternion.Euler(0.0f, 90.0f, 0.0f) * _interpolatedDirection;
}
else if (angle.AlmostEquals(0.0f, angleTolerance) == true)
{
targetDirection = Quaternion.Euler(0.0f, -90.0f, 0.0f) * _interpolatedDirection;
}
else if (Mathf.Abs(angle).AlmostEquals(180.0f, angleTolerance) == true)
{
targetDirection = Quaternion.Euler(0.0f, 90.0f, 0.0f) * _interpolatedDirection;
}
}
float realVelocityMagnitude = kccFixedData.RealVelocity.OnlyXZ().magnitude;
float desiredVelocityMagnitude = kccFixedData.DesiredVelocity.OnlyXZ().magnitude;
float kinematicVelocityMagnitude = kccFixedData.KinematicVelocity.OnlyXZ().magnitude;
targetMagnitude = Mathf.Min(realVelocityMagnitude, Mathf.Max(kinematicVelocityMagnitude, desiredVelocityMagnitude));
}
else
{
if (kccRenderData.RealVelocity.OnlyXZ().IsAlmostZero(0.025f) == false)
{
targetDirection = kccRenderData.RealVelocity.OnlyXZ().normalized;
targetMagnitude = kccRenderData.RealSpeed;
}
else
{
targetMagnitude = 0.0f;
}
}
_interpolatedDirection = Vector3.Slerp(_interpolatedDirection, targetDirection, _directionSmoothingSpeed * Time.deltaTime).normalized;
_interpolatedMagnitude = Mathf.Lerp(_interpolatedMagnitude, targetMagnitude, _magnitudeSmoothingSpeed * Time.deltaTime);
}
private float GetMaxBaseSpeed(Vector2 localNormalizedDirection)
{
if (localNormalizedDirection == Vector2.zero)
return 0.0f;
int setID = GetSetID();
if (setID < 0)
return 0.0f;
BlendTreeNode[] nodes = Sets[setID].Nodes;
int fromNodeIndex;
int toNodeIndex;
float alpha;
float angle = Vector2.Angle(localNormalizedDirection, Vector2.up);
if (angle >= 0.0f)
{
if (angle <= 45.0f) { fromNodeIndex = 1; toNodeIndex = 6; alpha = Mathf.Clamp01((angle - 0.0f) / 45.0f); }
else if (angle <= 90.0f) { fromNodeIndex = 6; toNodeIndex = 4; alpha = Mathf.Clamp01((angle - 45.0f) / 45.0f); }
else if (angle <= 135.0f) { fromNodeIndex = 4; toNodeIndex = 8; alpha = Mathf.Clamp01((angle - 90.0f) / 45.0f); }
else { fromNodeIndex = 8; toNodeIndex = 2; alpha = Mathf.Clamp01((angle - 135.0f) / 45.0f); }
}
else
{
if (angle >= -45.0f) { fromNodeIndex = 1; toNodeIndex = 5; alpha = Mathf.Clamp01((angle + 0.0f) / -45.0f); }
else if (angle >= -90.0f) { fromNodeIndex = 5; toNodeIndex = 3; alpha = Mathf.Clamp01((angle + 45.0f) / -45.0f); }
else if (angle >= -135.0f) { fromNodeIndex = 3; toNodeIndex = 7; alpha = Mathf.Clamp01((angle + 90.0f) / -45.0f); }
else { fromNodeIndex = 7; toNodeIndex = 2; alpha = Mathf.Clamp01((angle + 135.0f) / -45.0f); }
}
return Mathf.Lerp(nodes[fromNodeIndex].Position.magnitude, nodes[toNodeIndex].Position.magnitude, alpha);
}
private float GetMultiplier()
{
switch (_weapons.CurrentWeaponSlot)
{
case 0: { return 1.0f; }
case 1: { return 0.95f; }
case 2: { return 0.9f; }
}
return 0.95f;
}
}
}