Components/CharctorController.cs
using Sandbox;
using Sandbox.Citizen;


public sealed class CharacterController1 : Component
{
	[Property] public float GroundControl { get; set; } = 4.0f;
	[Property] public float AirControl { get; set; } = 0.1f;
	[Property] public float MaxForce { get; set; } = 50f;
	[Property] public float Speed { get; set; } = 160f;
	[Property] public float RunSpeed { get; set; } = 290f;
	[Property] public float CrouchSpeed { get; set; } = 90f;
	[Property] public float JumpForce { get; set; } = 400f;
	[Property] public GameObject Head { get; set; }
	[Property] public GameObject Body { get; set; }
	public Vector3 WishVelocity { get; set; }
	public bool IsCrouching = false;
	public bool IsSprinting = false;
	public bool canUnCrouch = false;
	private CharacterController characterController;
	private CitizenAnimationHelper animationHelper;
	protected override void OnAwake()
	{
		base.OnAwake();
		characterController = Components.Get<CharacterController>();
		animationHelper = Components.Get<CitizenAnimationHelper>();
	}
	protected override void OnUpdate()
	{
		base.OnUpdate();
		UpdateCrouch();
		IsSprinting = Input.Down( "Run" );
		RotateBody();
		if ( Input.Pressed( "Jump" ) ) Jump();
		UpdateAnimations();
	}
	protected override void OnFixedUpdate()
	{
		base.OnFixedUpdate();
		BuildWishVelocity();
		Move();
	}
	void BuildWishVelocity()
	{
		WishVelocity = 0;

		var rot = Head.Transform.Rotation;
		if ( Input.Down( "Forward" ) ) WishVelocity += rot.Forward;
		if ( Input.Down( "Backward" ) ) WishVelocity += rot.Backward;
		if ( Input.Down( "Left" ) ) WishVelocity += rot.Left;
		if ( Input.Down( "Right" ) ) WishVelocity += rot.Right;

		WishVelocity = WishVelocity.WithZ( 0 );

		if ( !WishVelocity.IsNearZeroLength ) WishVelocity = WishVelocity.Normal;

		if ( IsCrouching ) WishVelocity *= CrouchSpeed; // Crouching takes presedence over sprinting
		else if ( IsSprinting ) WishVelocity *= RunSpeed; // Sprinting takes presedence over walking
		else WishVelocity *= Speed;
	}
	void Move()
	{
		// Get gravity from our scene
		var gravity = Scene.PhysicsWorld.Gravity;

		if ( characterController.IsOnGround )
		{
			// Apply Friction/Acceleration
			characterController.Velocity = characterController.Velocity.WithZ( 0 );
			characterController.Accelerate( WishVelocity );
			characterController.ApplyFriction( GroundControl );
		}
		else
		{
			// Apply Air Control / Gravity
			characterController.Velocity += gravity * Time.Delta * 0.5f;
			characterController.Accelerate( WishVelocity.ClampLength( MaxForce ) );
			characterController.ApplyFriction( AirControl );
		}

		// Move the character controller
		characterController.Move();

		// Apply the second half of gravity after movement
		if ( !characterController.IsOnGround )
		{
			characterController.Velocity += gravity * Time.Delta * 0.5f;
		}
		else
		{
			characterController.Velocity = characterController.Velocity.WithZ( 0 );
		}
	}
	void RotateBody()
	{
		if ( Body is null ) return;
		var targetAngle = new Angles( 0, Head.Transform.Rotation.Yaw(), 0 ).ToRotation();
		float rotateDiffrence = Body.Transform.Rotation.Distance( targetAngle );
		if ( rotateDiffrence > 10f || characterController.Velocity.Length > 10f )
		{
			Body.Transform.Rotation = Rotation.Lerp( Body.Transform.Rotation, targetAngle, Time.Delta * 2f );
		}
	}
	void Jump()
	{
		if ( !characterController.IsOnGround ) return;
		characterController.Punch( Vector3.Up * JumpForce );
		animationHelper?.TriggerJump();
	}
	void UpdateAnimations()
	{ 
		if(animationHelper is null) return;
		animationHelper.WithWishVelocity( WishVelocity );
		animationHelper.WithVelocity( characterController.Velocity );
		animationHelper.AimAngle = Head.Transform.Rotation;
		animationHelper.IsGrounded = characterController.IsOnGround;
		animationHelper.WithLook( Head.Transform.Rotation.Forward, 1f, 0.75f, 0.5f );
		animationHelper.MoveStyle = CitizenAnimationHelper.MoveStyles.Run;
		animationHelper.DuckLevel = IsCrouching ? 1f : 0f;

	}
	void UpdateCrouch()
	{ 
		if ( characterController is null ) return;

		if ( Input.Pressed( "Duck" ) && !IsCrouching)
		{ 
			IsCrouching = true;
			characterController.Height /= 2f;
		}
		var duckTrace = characterController.TraceDirection( Vector3.Up * characterController.Height );
		if ( duckTrace.Hit )
		{
			canUnCrouch = false;
		}
		else {
			canUnCrouch = true;
		}
		if ( Input.Released( "Duck" ) && IsCrouching && canUnCrouch ) //| IsCrouching && !Input.Pressed( "Duck" ) && canUnCrouch 
		{ 
			IsCrouching = false;
			characterController.Height *= 2f;
		}
		if ( IsCrouching && !Input.Down( "Duck" ) && canUnCrouch )
		{
			IsCrouching = false;
			characterController.Height *= 2f;
		}
	}
}