282 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			282 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
|  | 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; | ||
|  | 		} | ||
|  | 	} | ||
|  | } |