As long as we’re tied to Update(), we’re held hostage by the variable frame rate.
Now for a dirty little secret (it’s not a secret):
In Unity, all physics is processed (collisions, the actual CharacterController movement, Rigidbody moves) takes place in a different Update, called FixedUpdate, which Unity makes every effort imaginable to ensure happens at the same rate no matter what the actual FPS is.
So in my own version of the project (and this will likely find it’s way into an RPG Core Combat rewrite) I add another method to the State class,
void FixedTick(float fixedTime);
And in my StateMachine, in FixedTick, I call
currentState?.FixedTick(Time.fixedDeltaTime);
Then I calculate movement (poll the InputReader) in Update and get a movement value, but wait until FixedUpdate to call Move().
So FixedUpdate() => FixedTick(float) may be the best place to put your applied force.