Myself, I like to solve this by thinking of all player movement as acceleration-based.
I choose a target velocity using whatever complicated control logic I like, then ask the player avatar to accelerate toward that target, while respecting maximum acceleration rates I set.
Then, depending on the avatar's state (on ground, on ice, in the air, in a knockback state), I can change those acceleration rates to make the control input have a sharper or less pronounced impact.
Rigidbody2D body;
void AccelerateTowards(Vector2 targetVelocity, float maxAccel, float maxDecel) {
// Compute desired velocity change.
var velocity = body.velocity;
var deltaV = targetVelocity - velocity;
// Convert our velocity change to a desired acceleration,
// aiming to complete the change in a single time step.
// (For best consistency, call this in FixedUpdate,
// and deltaTime will automatically give fixedDeltaTime)
var accel = deltaV / Time.deltaTime;
// Choose an acceleration limit depending on whether we're
// accelerating further in a similar direction, or braking.
var limit = Dot(deltaV, velocity) > 0f ? maxAccel : maxDecel;
// Enforce our acceleration limit, so we never exceed it.
var force = body.mass * Vector2.ClampMagnitude(accel, limit);
// Apply the computed force to our body.
body.AddForce(force, ForceMode2D.Force);
}
Separating acceleration & deceleration rates like this lets me give a sharper braking force, which tends to help the controls feel tight & responsive when stopping or changing directions, while keeping a smooth acceleration for getting up to speed. It also lets you penalize braking specifically when on ice or being knocked back.
We can make a control state parameters object to hold the max speed, acceleration, and deceleration rates for our air, ground, ice, knockback states etc. and use this to adjust our control handling very flexibly:
// Choose our current speed / acceleration parameters based on our state.
var controlState = GetCurrentControlState();
// Compute desired velocity.
var velocity = GetDesiredVelocityFromInput(controlState.maxSpeed);
// Try our best to reach that velocity, with our current parameters.
AccelerateTowards(
velocity,
controlState.maxAccel,
controlState.maxDecel
);