Runtime Character Speed: Sprint, Slows, and Safe Restoration¶
A sprint button is easy until a slow, crouch, speed pickup, or network game touches the same value. The durable pattern is to keep the reasons for the speed separate, calculate one effective limit, and let Character Movement turn input into actual motion.
At a glance
Main target: your Character's Character Movement component ·
Main value: Max Walk Speed, in centimeters per second ·
Returns: property setters return nothing and do not report success ·
Does not mean: current speed ·
Official references:
UCharacterMovementComponent
and Add Movement Input
The one-minute version¶
- Max Walk Speed is the usual cap for ground movement. A normal Unreal
unit is one centimeter, so
600means 600 centimeters per second. - Max Acceleration controls how quickly velocity changes toward the input request. It is not another top-speed value.
- Braking Deceleration Walking and friction control how velocity falls when there is no acceleration, and how an over-speed Character returns toward its cap.
Add Movement Inputsupplies direction and input strength. Making its Scale Value larger does not replace Max Walk Speed with a higher sprint speed.- Do not save "whatever Max Walk Speed is now" on sprint press and restore it on release. That saved float becomes stale if another system changes speed before release.
- Keep
BaseWalkSpeed, sprint state, and slow/buff modifiers as separate data. Send every change through oneRecalculateMovementSpeedfunction. - In multiplayer, the server owns the accepted gameplay state.
Max Walk Speedis not a general replicated gameplay variable by itself, so replicate or otherwise synchronize the state used to calculate it. - Animation should read actual velocity, usually
Vector Length XY, not treat the configured maximum as the speed the Character achieved.
The four values beginners mix together¶
These settings cooperate, but they answer different questions:
| Value | Unit | Question it answers |
|---|---|---|
| Max Walk Speed | cm/s | How fast may ordinary walking or running get? It also limits lateral falling speed. |
| Max Walk Speed Crouched | cm/s | What ground-speed cap applies while the Character is crouched? |
| Max Acceleration | cm/s² | How quickly may velocity change toward the requested movement direction? |
| Braking Deceleration Walking | cm/s² | How strongly may walking velocity slow when there is no acceleration? |
Ground friction and braking-friction settings also affect starts, turns, and stops. A higher maximum does not guarantee an instant sprint: low acceleration can make the Character take time to reach it. Lowering the maximum does not teleport velocity to the new value either; the movement calculation brings an over-speed Character back toward the allowed speed.
The current movement mode chooses the relevant family of limits. Walking and nav walking use the ground settings; crouched walking uses Max Walk Speed Crouched; swimming, flying, and custom movement have their own settings or logic. Max Walk Speed also caps lateral motion while falling, but it does not replace gravity or vertical velocity.
How input becomes motion¶
For a normal Character, the useful mental model is:
input action value
-> Add Movement Input builds a pending input vector
-> Character Movement turns that vector into acceleration
-> movement mode, Max Acceleration, friction, and braking update velocity
-> the current mode's maximum limits the result
-> collision and floor rules decide where the capsule can actually move
Scale Value = 1 means full input strength in the supplied direction. An
analog stick at partial tilt can request less than full movement. Character
Movement also has Min Analog Walk Speed, which is the low-end ground speed
used for small non-zero analog input. None of those input values raise the
configured maximum.
That is why this is the wrong sprint graph:
Sprint pressed
-> Add Movement Input with Scale Value = 2
It changes the request, not the owning movement limit. With full keyboard input the request was already full strength, so the Character still obeys the same Max Walk Speed.
The pattern everyone actually uses¶
Give speed one owner. Store the inputs to the calculation, then calculate the component property in one place.
Start with these variables on the Character or a reusable movement-state component:
| Variable | Example | Meaning |
|---|---|---|
BaseWalkSpeed |
500 |
The design speed before temporary modifiers. |
SprintMultiplier |
1.5 |
The multiplier used when sprint is accepted. |
bWantsToSprint |
false |
The player's current request. |
bCanSprint |
true |
Gameplay permission after stamina, weapon, status, and mode checks. |
SlowMultiplier |
1.0 |
The combined active slow, where 0.6 means 60% speed. |
BuffMultiplier |
1.0 |
The combined active speed buffs. |
MinAllowedWalkSpeed |
0 |
The lower clamp. Zero permits a full speed lock; use a positive value if the project forbids tiny speeds. |
MaxAllowedWalkSpeed |
1200 |
A project rule that caps stacked bonuses. |
Then make one function:
pseudocode for a Blueprint function - not engine source
RecalculateMovementSpeed():
sprintFactor = (bWantsToSprint AND bCanSprint) ? SprintMultiplier : 1.0
effective = BaseWalkSpeed
* sprintFactor
* SlowMultiplier
* BuffMultiplier
effective = Clamp(effective, MinAllowedWalkSpeed, MaxAllowedWalkSpeed)
CharacterMovement.MaxWalkSpeed = effective
Call that function whenever any input changes:
IA_Sprint Started
-> set bWantsToSprint = true
-> RecalculateMovementSpeed
IA_Sprint Completed or Canceled
-> set bWantsToSprint = false
-> RecalculateMovementSpeed
slow added, removed, or refreshed
-> rebuild SlowMultiplier from active effects
-> RecalculateMovementSpeed
stamina reaches zero, weapon state changes, or movement mode changes
-> update bCanSprint
-> RecalculateMovementSpeed
Use Started plus Completed/Canceled for a held Enhanced Input action.
Triggered can run every update while held; repeatedly writing the same value
is usually unnecessary and makes debugging noisy.
Why restoring a cached float breaks¶
Consider this sequence:
normal speed = 500
sprint pressed -> cache 500, set 750
mud slow begins -> set 300
sprint released -> restore cached 500
The release silently erased the mud slow. Reversing the event order can erase a buff instead. The bug is not event timing; it is that several systems are using one mutable float as both state and result.
With recomputation, sprint release changes only bWantsToSprint. The still
active slow remains an input, so the effective value stays slowed.
More than one slow or buff¶
A single SlowMultiplier is enough only if one effect can exist at a time.
When effects can overlap, keep an array, map, Gameplay Tag-driven state, or
another explicit collection of active modifiers. Rebuild the combined value
when the collection changes.
Decide the stacking rule as game design:
- multiplying
0.8 * 0.5produces a 40% final factor; - choosing the strongest slow would produce
0.5instead; - adding percentages needs an explicit lower and upper clamp.
There is no universally correct formula. The important invariant is that the formula lives in one function and every system contributes data rather than writing Max Walk Speed independently.
Crouch and movement mode are separate inputs¶
Character Movement selects Max Walk Speed Crouched while crouched. Changing Max Walk Speed alone does not automatically change that property.
Choose a deliberate policy:
- use a separately authored
BaseCrouchedSpeedand apply the same global slow/buff factors to Max Walk Speed Crouched; - disable sprint permission while crouched; or
- allow a distinct crouch-sprint multiplier if the game calls for it.
Do not copy the current standing Max Walk Speed into Max Walk Speed Crouched every update. That erases the reason the two settings exist.
Swimming, flying, and custom modes also have separate maximums. If a shared status effect should slow all movement, let one state owner calculate every affected mode-specific maximum. If the effect is "mud slows walking," change only the ground calculation.
What changing Max Walk Speed does not do¶
Writing the property:
- does not move the Character by itself;
- does not create movement input;
- does not instantly make actual velocity equal the new maximum;
- does not bypass collision, floor checks, movement mode, root motion, or an ignored input gate;
- does not change Max Walk Speed Crouched, Max Swim Speed, Max Fly Speed, or a custom mode's logic;
- does not choose a run animation;
- does not consume stamina; and
- does not automatically replicate your sprint/slow/buff rules.
The Blueprint property setter has no Boolean success output. On a valid
Character's built-in Character Movement component, it writes the float. If
you reached the node through an invalid Character or component reference,
Blueprint reports an Accessed None-style runtime error and skips useful work.
Guard external references; inside the Character Blueprint, use its built-in
component directly.
Multiplayer: synchronize the reason, not only the float¶
Character Movement predicts the owning player's motion and the server checks that motion. The server must therefore use the accepted sprint and modifier state when it reproduces moves.
For a beginner Blueprint implementation:
owning client starts or stops sprint
-> Server_SetWantsToSprint(requested state)
server validates stamina, status, and game rules
-> writes replicated sprint/effect state
-> calls RecalculateMovementSpeed on the server
RepNotify on clients
-> calls the same RecalculateMovementSpeed
The server should validate rather than accept an arbitrary speed float from
the client. Replicate bIsSprinting, a validated effective speed, or the small
set of authoritative modifiers your game needs. Which representation is best
depends on whether other systems need to understand why the speed changed.
Max Walk Speed itself is not a general-purpose replicated property. Setting it only on the owning client lets prediction use a value the server may reject, causing corrections. Setting it only on the server can leave the owning client predicting with an old value until your state reaches it. Apply the same authoritative result on every copy that simulates or presents the Character; advanced projects can predict the local sprint request and roll it back if the server rejects it.
Built-in Character Movement already transports and corrects normal movement. That does not make arbitrary new Blueprint state part of its saved-move protocol. A deeply predicted custom sprint or ability system may need a custom Character Movement implementation, but a server-validated replicated state is the honest beginner boundary.
Animation reads the result¶
An Animation Blueprint usually wants motion, not permission:
OwnerCharacter
-> Get Velocity
-> Vector Length XY
-> GroundSpeed
Why not read Max Walk Speed?
- the Character may still be accelerating;
- collision may block it;
- it may be braking or sliding;
- analog input may request less than full speed;
- it may be falling, on a moving base, or under root motion; and
- the maximum can change before velocity catches up.
Use actual velocity for locomotion blend spaces. Read replicated gameplay
state such as bIsSprinting only when the animation needs the intent as well,
for example a sprint-start pose that should begin before full speed is reached.
A practical debugging ladder¶
When sprint or a slow "does nothing," inspect in this order:
- Input request: Does the sprint action fire on the intended local player?
- State inputs: What are
bWantsToSprint,bCanSprint, and every active multiplier on this Character instance? - Calculated result: Put one breakpoint in
RecalculateMovementSpeed. Which value did it produce? - Component value: Read Max Walk Speed immediately after the function. Did another graph overwrite it later?
- Movement mode and crouch: Is the Character using a different maximum?
- Actual motion: Watch Velocity and
Vector Length XY. A configured cap is not proof of motion. - Acceleration and braking: Is the result merely taking time to change?
- Collision and input gates: Is the capsule blocked, unpossessed, or ignoring movement input?
- Network copy: In multiplayer Play In Editor, are you watching the owning client, server, or a simulated proxy, and do their calculated values agree?
If Max Walk Speed changes and immediately changes back, search the Blueprint for every setter. Runtime initialization, crouch code, stamina code, pickups, and animation-event experiments are common competing writers.
Lookalikes - which control do I want?¶
| Control | Use it for | It is not |
|---|---|---|
| Max Walk Speed | The standing ground-speed limit. | Current velocity or input strength. |
| Max Walk Speed Crouched | The crouched ground-speed limit. | A crouch request. |
| Max Acceleration | How quickly velocity can change toward input. | A maximum velocity. |
| Braking Deceleration Walking | No-input walking slowdown. | A hard stop. |
| Add Movement Input | A per-update directional request. | A sprint-speed setter. |
| Get Velocity | The motion result used by animation and debugging. | A movement limit. |
| Set Actor Location | An intentional placement or teleport. | Predicted Character walking. |
| Global Time Dilation / Custom Time Dilation | Changing simulation time scale. | A clean substitute for gameplay movement modifiers. |
Going deeper¶
- Character Movement Component: the knobs that matter - the wider movement, jump, braking, rotation, and networking system.
- Add Movement Input - why input strength is not speed.
- Get Velocity and Vector Length / Vector Length XY - measuring the result for animation and debugging.
- Enhanced Input trigger states - choosing Started, Completed, and Canceled for a held sprint action.
- Replication basics and RepNotify variables and local presentation - server-owned state and client updates.
- Official docs: UCharacterMovementComponent, Add Movement Input, and Networked Character Movement.
- Engine source (requires engine access - we do not reproduce it here):
UCharacterMovementComponent::GetMaxSpeed,CalcVelocity, andApplyVelocityBrakinginEngine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp; the speed, acceleration, and braking properties live inEngine/Source/Runtime/Engine/Classes/GameFramework/CharacterMovementComponent.h.