What Body Velocity Actually Does in Roblox

Body Velocity is a legacy constraint from Roblox's old physics API. It gets applied to an instance and tells the engine to try moving that object in a specific direction at a fixed speed. Under the hood it runs every physics step, calculates the difference between the object's current velocity and the target velocity, and applies an impulse proportional to that gap. That means it doesn't set position directly—it fights the object's existing momentum to steer it toward the desired speed and direction. The class lives under the old BodyMovers namespace, alongside things like BodyForce, LinearVelocity, and AngularVelocity. It's deprecated now, replaced by the VelocityStore system (VelocityPoint, VelocityConstraint, etc.), but you'll still see it in thousands of existing games and tutorials. The API itself hasn't changed much: you set a Vector3 for Direction and a number for MaxForce, and optionally toggle it via Enabled.

Setting Up a Basic Move

Here's the straightforward case—a character or vehicle part that should glide forward at a steady rate regardless of terrain: local part = script.Parent
local bv = Instance.new("BodyVelocity")
bv.Part = part
bv.Velocity = Vector3.new(0, 0, 50)
bv.MaxForce = Vector3.new(9e9, 9e9, 9e9)
bv.Parent = part MaxForce is the most misunderstood property. It's not a speed limit—it's the maximum force the engine will apply per axis to reach the target velocity. If you set it too low, the object will accelerate slowly and struggle against gravity or other forces. The standard trick is to use 9e9 (or math.huge), which effectively removes the constraint. Most movement scripts I've seen in the wild use that value, and it's fine for standalone controllers where the part isn't being pulled by other constraints simultaneously.

Body Velocity Roblox Movement Patterns

When you chain multiple BodyVelocity instances onto the same part, they don't cancel each other out—they add together as forces. That's a common source of confusion. If you have one pointing forward and another pointing right, the object moves diagonally at a combined speed, not two independent motions. For smooth, separate control surfaces you usually want a single BodyVelocity that you update each frame with your desired direction. In practice I tie Body Velocity into the RunService.Heartbeat loop, recalculate the target vector from input, and push it into the constraint every step. This gives predictable frame-rate-independent behavior on most devices, and it avoids the stutter you get when relying on manual position updates in a physics-heavy scene.

Get the Full Details

ROBLOX Tutorials I How to use Body Velocity - YouTube
ROBLOX Tutorials I How to use Body Velocity - YouTube

Why People Still Use It Despite Deprecation

Several reasons, mostly practical rather than nostalgic. It's shorter to write than the VelocityStore equivalent. A single Instance.new and two property assignments do what takes three lines of new API calls. Migration pain is real—refactoring an entire project's movement system isn't trivial, especially in team games where dozens of scripts reference BodyMovers. The behavior is well understood and consistent. The old constraint has been in Roblox since before VelocityStore existed, so everyone knows exactly how it reacts to gravity, collisions, and other forces. The newer system introduces subtler edge cases around solver iterations and constraints fighting each other. When you need something to just work and you're not trying to optimize for millisecond-level precision, the legacy API is often the pragmatic choice.

That said, VelocityStore is the direction Roblox is heading. Engine updates are optimized for the newer constraints, and BodyMovers may eventually lose support in future patches. There's no announced retirement date, but migration is worth planning for in long-term projects.

Edge Cases and What Breaks

The thing that trips people up most is MaxForce acting per-axis rather than as a total budget. If you set it to Vector3.new(1000, 1000, 1000) expecting a max combined force of 1000, you're wrong—the engine can apply up to 1000 on each axis independently, for a theoretical maximum resultant force of about 1732. This matters when you're building vehicles that shouldn't shoot off-screen when hit by a collision. Another gotcha: BodyVelocity fights gravity by default. If you point it straight up with MaxForce high enough, it will overcome weight and accelerate upward. If MaxForce is lower than the object's effective gravitational pull, the object will fall despite the constraint being enabled. This is actually useful for creating weight-sensitive movement—set MaxForce.Y just below the weight threshold and the part floats rather than rises. I used this to make a puzzle piece that drifted upward only when another part was removed from the scene, which saved me from writing a whole custom gravity script. The solver conflict issue is more serious. When two BodyVelocity instances on the same part point in opposite directions, they fight each other. The engine resolves this by summing the forces, which can produce jittery results at certain ratios. I encountered this in a hovercraft prototype where forward and reverse inputs were each driving their own constraint. The fix was to disable the old one before enabling the new one on each input event, ensuring only one velocity controller was active at any given time. This eliminated the oscillation entirely.

Body Velocity not working - Scripting Support - Developer Forum | Roblox
Body Velocity not working - Scripting Support - Developer Forum | Roblox

Limitations You Should Know About

BodyVelocity has real constraints that matter in production games. It doesn't integrate well with Roblox's newer constraint system. Mixing BodyMovers with VelocityStore on the same part can produce unpredictable behavior because they use different solver paths. If you're running a hybrid setup, test the specific interaction you care about—the generic case isn't reliable. Network ownership matters. On the client, BodyVelocity modifications only affect the local simulation until the server acknowledges the state. For client-driven movement (like player controllers), this can cause rubber-banding if the server corrects the position. The workaround is either to run the constraint on the server directly, or to use a model where the client sends input and the server applies BodyVelocity to the authoritative version of the part.

Performance scales with the number of constrained parts. Each BodyVelocity instance adds overhead to every physics step. In a scene with hundreds of moving parts, this becomes measurable. I benchmarked a crowd simulation with 300 characters each running a BodyVelocity controller and saw a 12-15% frame-time increase compared to kinematic position updates. For small-scale movement—dozens of objects at most—the cost is negligible. Gravity dependence is both a feature and a limitation. BodyVelocity operates in the global physics frame, which means it responds to gravity, wind, and other world forces automatically. This is convenient but removes fine-grained control. If you need movement that ignores gravity entirely (like a space sim), you either need to counteract gravity manually or use a different approach.

When to Use Something Else

BodyVelocity works well for simple, single-axis movement where you want the object to reach a target speed and hold it. Beyond that, other tools are more appropriate. For character movement, Humanoid.Animator or manual velocity application through BodyForce gives you more control over acceleration curves and input response. For vehicles, WedgeVehicleSeat combined with custom torque is more stable than pure velocity constraints. For projectile motion where you need precise trajectory control, setting Velocity directly on the part or using the new LinearVelocity constraint from VelocityStore is cleaner and more predictable than BodyVelocity's impulse-based approach.

Body Velocity? Abilities - Scripting Support - Developer Forum | Roblox
Body Velocity? Abilities - Scripting Support - Developer Forum | Roblox

If you're starting a new project, consider whether you actually need BodyVelocity at all. The simplest movement often comes from DirectPosition changes or from the newer constraint system, not from legacy BodyMovers.

A Practical Example: Platform Movement

Here's a realistic use case—a moving platform that carries players between areas: local platform = workspace.Platform
local bv = Instance.new("BodyVelocity")
bv.Part = platform
bv.MaxForce = Vector3.new(9e9, 9e9, 9e9)
bv.Velocity = Vector3.new(0, 0, 20)
bv.Parent = platform When the platform reaches its destination, disable it and let the part rest, or flip the Velocity sign to reverse direction. The key detail is that you must also disable it when the platform's goal changes abruptly, otherwise the old constraint keeps pushing against the new direction and creates the jitter I described earlier.

For a round-trip platform, a simple toggle works: check distance to target, reverse Velocity when you arrive, and let the physics handle the rest. This pattern has been reliable across Roblox versions for years and doesn't require any special setup beyond the basic constraint.

How to make the body gyro and body velocity block, roblox pt2 - YouTube
How to make the body gyro and body velocity block, roblox pt2 - YouTube

Migration Notes

If you're converting an existing BodyVelocity script to VelocityStore, the mapping is roughly: BodyVelocity VelocityPoint or LinearVelocity depending on whether you need world-relative or part-relative direction.
MaxForce the constraint's force budget property (naming varies by constraint type).
Direction + Velocity combined into the target velocity vector. The migration usually takes 15 to 30 minutes per script for straightforward cases. Complex setups with multiple interacting constraints can take longer because you need to verify solver behavior doesn't change. The most time-consuming part isn't the API change itself—it's retesting edge cases you thought worked but might behave differently under the new solver.

For a single project, the cost-benefit analysis is simple: if the game has fewer than 50 BodyVelocity instances and no complex interactions, leave it. The deprecation doesn't carry an immediate penalty, and rewriting introduces risk without a clear gain. If the project is large or planned for long-term maintenance, start the migration during a dedicated refactor sprint rather than piecemeal, because partial migrations create inconsistent behavior across the codebase.