public void FixedUpdate()
{
    Vector3 movementDirection = new Vector3(_playerActions.CurrentMovementDirection.x, 0, _playerActions.CurrentMovementDirection.y);
    Vector3 movementVelocity = movementDirection * _context.Config.GroundControlMult;
    Vector3 movementDisplacement = movementVelocity * Time.fixedDeltaTime;

    Vector3 currentPosition = _context.Rigidbody.position;
    float colliderRadius = _context.Collider.radius;
    float colliderHeight = _context.Collider.height;

    Vector3 lastNormal = Vector3.zero;

    for (int i = 0; i < KCCUtils.MAX_WALL_CHECK_ITERATIONS; i++)
    {
        if (KCCUtils.CheckWillBeAgainstWall(currentPosition, colliderRadius, colliderHeight, movementDisplacement.normalized, movementDisplacement.magnitude, out RaycastHit hit))
        {
            float distanceToWall = Mathf.Max(hit.distance - KCCUtils.SKIN_WIDTH, 0);

            Vector3 movementToWall = movementDisplacement.normalized * distanceToWall;
            Vector3 movementInsideWall = movementDisplacement - movementToWall;

            currentPosition += movementToWall;

            // We've hit 1 plane. Slide along it.
            if (i == 0)
            {
                movementDisplacement = Vector3.ProjectOnPlane(movementInsideWall, hit.normal);
            }
            
            // We've hit 2 planes. The only legal movement is along their intersection.
            if (i == 1)
            {
                Vector3 crossProduct = Vector3.Cross(lastNormal, hit.normal);
                Vector3 creaseVector = crossProduct.normalized;

                movementDisplacement = Vector3.Project(movementInsideWall, creaseVector);
            }

            // We've hit 3 planes. The only legal movement is away from all 3.
            if (i == 2)
            {
                movementDisplacement = Vector3.zero;
            }

            lastNormal = hit.normal;
        }
        else
        {
            currentPosition += movementDisplacement;
            break;
        }
    }

    _context.Rigidbody.MovePosition(currentPosition);
}



// Wall check
public static bool CheckWillBeAgainstWall(Vector3 startPosition, float radius, float height, Vector3 movementDirection, float maxDistance, out RaycastHit hit)
{
    radius -= SKIN_WIDTH;

    Vector3 topSphereOrigin = startPosition + Vector3.up * ((height / 2) - radius);
    Vector3 bottomSphereOrigin = startPosition + Vector3.down * ((height / 2) - radius);

    return Physics.CapsuleCast(topSphereOrigin, bottomSphereOrigin, radius, movementDirection, out hit, maxDistance);
}