public void Simulate(
            CharacterController characterController, PlayerFoley playerFoley, PlayerInput input)
        {
            var   transform = this.transform;
            float deltaTime = Time.deltaTime;

            bool isJumping = !characterController.isGrounded;
            bool jumpStart = false;

            if (!isJumping && input.jump)
            {
                _jumpingScalar = -jumping.force;
                jumpStart      = true;
            }
            else if ((_jumpingScalar += deltaTime * jumping.dampSpeed) >= jumping.gravityFactor)
            {
                _jumpingScalar = jumping.gravityFactor;
            }

            float frictionFactor = isJumping ? 0.99f : 0.1f;
            var   absMove        = new Vector2(Mathf.Abs(input.move.x), Mathf.Abs(input.move.y));
            var   signMove       = new Vector2(Mathf.Sign(input.move.x), Mathf.Sign(input.move.y));

            var wantSpeed = Vector2.one;

            wantSpeed *= Mathf.Lerp(locomotion.walkSpeed, locomotion.runSpeed, input.run);
            wantSpeed  = Vector2.Scale(wantSpeed, signMove);
            wantSpeed  = Vector2.Lerp(_movingSpeed, wantSpeed, isJumping ? 0f : deltaTime * 10f);
            wantSpeed  = Vector2.Scale(
                wantSpeed,
                Vector2.Lerp(
                    absMove,
                    new Vector2(
                        Mathf.Approximately(input.move.x, 0f) ? 0f : 1f,
                        Mathf.Approximately(input.move.y, 0f) ? 0f : 1f),
                    input.run));
            _movingSpeed = Vector2.Lerp(wantSpeed, wantSpeed * frictionFactor, !isJumping ? 0f : deltaTime);

            float angularSpeed = 360f * deltaTime * Mathf.Lerp(looking.lookSpeed, looking.runLookSpeed, input.run);
            float lookX        = input.look.x;
            float lookY        = input.look.y * Mathf.Sign(input.move.y);

            _lookingAngles.x = _lookingAngles.x + lookX * angularSpeed;
            _lookingAngles.y = _lookingAngles.y + lookY * angularSpeed;

            _lookingAngles.x = Angles.Unwind(
                Mathf.Clamp(
                    Angles.ToRelative(_lookingAngles.x),
                    looking.pitchLimitMin,
                    looking.pitchLimitMax));

            UpdateLooking(transform, deltaTime);
            UpdateMoving(transform, deltaTime, characterController);

            float walkSpeedSqr = locomotion.walkSpeed * locomotion.walkSpeed;
            float runSpeedSqr  = locomotion.runSpeed * locomotion.runSpeed;
            float speedSqr     = _movingSpeed.sqrMagnitude;
            float speedScalar  = (speedSqr - walkSpeedSqr) / (runSpeedSqr - walkSpeedSqr);

            if (speedScalar < Mathf.Epsilon)
            {
                speedScalar = 0f;
            }

            int hadFeetPlanted = _feetPlanted;

            isJumping = !characterController.isGrounded; // check again after moving

            if (jumpStart)
            {
                _jumpSpeedScalar = speedScalar;
            }

            UpdateFootPlanting(transform, playerFoley, speedSqr, speedScalar, isJumping);

            if (jumpStart)
            {
                playerFoley.OnJump();
            }
            else if (hadFeetPlanted == 0 && _feetPlanted != 0)
            {
                playerFoley.OnLand();
            }

            playerFoley.breathingIntensity = isJumping ? 1f : speedScalar;
            playerFoley.playerJumping      = jumpStart || _feetPlanted == 0;
        }