Code/Demo/DemoCharacterAnimator.cs
using Andicraft.SecondOrderDynamics;
using Sandbox;

[Title("[DEMO] Character Animator"), Category("Second Order Dynamics (Demo)")]
public class DemoCharacterAnimator : Component
{
	private PlayerController _playerController;
	
	#region Body

	[Feature("Body"), Property] public GameObject BodyObject { get; set; }
	[Feature("Body"), Property] public float BodyHeightOffset { get; set; }
	[Feature("Body"), Property] public float BodyCrouchOffset { get; set; }
	[Feature("Body"), Property] public DynamicsParameters BodyPosParameters { get; set; } = new();
	[Feature("Body"), Property] public DynamicsParameters BodyRotParameters { get; set; } = new();

	private Vector3Dynamics _bodyPosDynamics;
	private RotationDynamics _bodyRotDynamics;

	#endregion

	#region Hands

	[Feature( "Hands" ), Property] public GameObject HandsContainer;
	
	[Feature("Hands"), Property] public DynamicsParameters HandsPosParameters { get; set; } = new();
	[Feature("Hands"), Property] public DynamicsParameters HandsRotParameters { get; set; } = new();

	private Vector3Dynamics _handsPosDynamics;
	private RotationDynamics _handsRotDynamics;
	
	#endregion
	
	
	protected override void OnAwake()
	{
		_bodyPosDynamics = new Vector3Dynamics( BodyPosParameters, WorldPosition + Vector3.Up * BodyHeightOffset );
		_bodyRotDynamics = new RotationDynamics( BodyRotParameters, WorldRotation );
		
		_handsPosDynamics = new Vector3Dynamics( HandsPosParameters, WorldPosition + Vector3.Up * BodyHeightOffset );
		_handsRotDynamics = new RotationDynamics( HandsRotParameters, WorldRotation );
		
		_playerController = GetComponent<PlayerController>();
	}

	private Vector3 _forward = Vector3.Forward;
	
	protected override void OnUpdate()
	{
		var vel = _playerController.Velocity;
		var horizontalVel = new Vector3( vel.x, vel.y, 0 );
		
		if (horizontalVel.Length > 3)
			_forward = horizontalVel.Normal;

		var targetLook = Rotation.LookAt( _forward );
		var dirToFeet = Rotation.FromToRotation(Vector3.Up, (BodyObject.WorldPosition - WorldPosition).Normal);

		var targetRot = Rotation.Identity * dirToFeet * targetLook;

		var h = _playerController.IsDucking ? BodyCrouchOffset : BodyHeightOffset;

		BodyObject.WorldRotation = _bodyRotDynamics.Update( Time.Delta, targetRot);
		BodyObject.WorldPosition = _bodyPosDynamics.Update( Time.Delta, WorldPosition + Vector3.Up * h );

		HandsContainer.WorldPosition = _handsPosDynamics.Update( Time.Delta, BodyObject.WorldPosition );
		HandsContainer.WorldRotation = _handsRotDynamics.Update( Time.Delta, BodyObject.WorldRotation );
	}
}