BodyVelocity❌ BodyForce✅ for Airplanes (Aerodynamics)

The issues you’re experiencing have to do with the limitations of discrete-time simulations. The laws of aerodynamics are defined in continuous time, which means time can be sliced up infinitely fine in order to create a valid path from one state to another. A fundamental issue in simulating continuous time physics on a computer is you have to pick finite, and usually constant, time step.
Instability comes from using too coarse of a time step. Not all problems allow you to pick a “small enough” time step either. Many flight sims use a step about 4 times higher than the framerate, so about 1/240th of a second. Essentially too large a step causes the rules to diverge from what your simulation actually shows. This can even be a source on un-conserved momentum or energy.

This is probably not relevant as you aren’t going to want to simulate fluids at such a low level, but I found this video very informative for thinking about aerodynamics and it might interest you: https://www.youtube.com/watch?v=IVLpbOQUdqU

1 Like

I don’t think that’s exactly what I meant, but alright.

I made a breakdown of how to implement realistic aircraft physics, as well as an example of its implementation. I think its got everything you need to understand how to approach something like this, and how to actually make it work. I hope it helps: Creating Realistic Plane Physics

Hey all. Thank you for all the replies, I apologize for being late here. Tried to extend my break.


I did it and it was a good start. I made it in a way the drag would only apply on the wide area and not the edges, locally. It did not only simulate the air resistance, but tilting the plate causes it to fall towards the lowest point as well.

2025-03-05-14-03-48-ezgif.com-cut


I think you overexplained it, but it was amazing. I had to read it several times within the timespan away from the forum since my brain wasn’t pairing with. A bit rough there, but I appreciate the effort you put here.


Tested the place. It’s good, but I found strange behaviors. Some of the scripts aren’t explained, I went for the Physics script . I’m unfamiliar to ModuleScripts, so I don’t recognize some of the language.


I’ve had taken a break from Roblox developing for too long, way before I made this topic. Looking at those numbers and calculations scared me.

I assume that BodyVelocity is not ideal for “realistic” aerodynamics, and BodyForce will do the job. Since they work by applying constant forces, they are known to send objects to beyond lightyears. And this is where drag force is important.

I’ve tried testing it in a single BasePart to see if they work, while also experimenting different approaches and combinations.

@Imperiector told me to get CL from AoA, but I don’t feel like writing the lookup table. So, I had to ask ChatGPT’s opinion—knowing most of my questions for Ro-deving resulted in failure :grimacing:

local airDensity = 1.2
local CL_alpha = 4.5 -- Depends on wing types, GPT said ¯\_(ツ)_/¯ (F-15 Eagle)
local CL_0 = 0.2 -- CL at zero AoA
local S = 2 --  "The reference area S can be any number"

local Q = airDensity * 0.5 * velocity.Magnitude^2 -- Dynamic pressure

function calculateLift(Q, AoA)
	local CL = (CL_alpha * AoA) + CL_0 -- GPT's formula
	local lift = CL * AoA * Q * S
	return lift
end

I’ve printed out the CL while the part goes in different directions, and… random, numbers. Somewhere between -13 to 7, I am clueless. I’ve made sure that AoA was correct.

Even worse with the lift. Went from negative tiny decimals to randomized, uneven numbers.

I am unable to calculate lift-induced drag without CL.

1 Like

For the lift equation, you dont want to directly use the AoA to calculate the lift force. The AoA is used to calculate the lift coefficient. To clarify, AoA in simulations should only be how much the plane’s nose(cord line) deviates from the velocity vector ON THE PITCH AXIS. This means we are not concerned with the yaw axis AoA. In my physics module i use this function:

function Physics.calculateLift(velocity, liftPower,  AOA)
	-- Lift = 0.5 * velocity^2 * liftcoefficient * liftpower(hand tuned)
	-- A curve sin(3x) derives the Lift Coefficient outputting values from 1 to -1 depending on the AOA.
	-- Only values from -60 to 60 aoa will output coefficiencients from -1-1 beyond is the stall region, where coefficient stays 0.1
	local liftCoefficient = math.clamp(math.sin(math.rad(3 * AOA)), -60, 60)
	local lift = 0.5 * velocity.Magnitude^2 * liftCoefficient * liftPower
	
	return lift/1000 
end

It might sound complicated, but its essentially using the AoA to output a value from -1 to 1, that will determine the lift force. Planes are designed to go up when they face up, and down when they face down. The lift force can be positive, or negative, and its strength is dependent on angle of attack. The lift coefficient in this scenario is calculated by a sin(3x) curve.


You can see how when we provide an angle of attack(lets say 30) to the sin(3x) equation, its giving us a value of 1. This means at 30 the lift force is at its maximum. You can also see that at -30 (30 degrees BELOW the velocity vector) AoA we get maximum NEGATIVE AoA. This way, the lift force will be pointing down when we are flying down, and pointing up when we fly up. Past 30 degrees, the lift force starts to fall off, and at 60 degrees, we are stalling, since the plane no longer generates lift.

So, to summarize, the lift equation IRL is Lift = CoefficientLift * wingArea * 0.5 * airdesnity * Velocity^2. Our equation is simplified, we ignore airdensity along with wing Area, and replace it with a custom tuned liftPower variable that can be changed for different planes. We effectively get: 0.5 * V^2 * LiftCoefficient * liftPower. Our lift coefficient will make the lift force positive when we are pointing our nose above the velocity vector, and negative when we point below it. This is how i calculated my AoA:

-- Oncoming wind is opposite to the direction of the velocity vector
	-- The AOA is calculated by getting the angle that the look vector deviates from the velocity vector
	if velocity.Magnitude > 0 then 
		-- Project the velocity vector onto the plane's vertical plane
		local projectedVelocity = velocity.Unit - CF.RightVector * velocity.Unit:Dot(CF.RightVector)

		-- Calculate the angle between the projected velocity and look vector
		local dotProduct = math.clamp(CF.LookVector:Dot(projectedVelocity.Unit), -1, 1)
		local cross = CF.LookVector:Cross(projectedVelocity.Unit)
		local sign = cross:Dot(CF.RightVector) > 0 and -1 or 1 -- Check if the aoa is pos/neg, if nose is above/below velocity vector

		-- Convert to degrees and apply neg or pos, clamp from -90 to 90 
		AOA = math.clamp(math.deg(math.acos(dotProduct)) * sign, -90, 90)
	else AOA = 0 end -- To avoid NaN AoAs

Sometimes we divide by 0 when our velocity is 0, and we get NaN AoA values, which breaks literally everything, so we have checks to see if we are above 0km/s. Velocity is a 3d vector pointing in the direction of travel the plane is moving in, and its MAGNITUDE is the speed it is traveling in that direction. When we say “if velocity.Magnitude > 0 then”, we are pretty much saying “only calculate the AoA when we are moving”. This way our physics functions that include AoA dont start crashing and printing errors.

Finally, in our AoA calculations, we are also outputting negative AoA. AoA is always say to be “the angle between the chord line of an airfoil (like an aircraft wing) and the direction of the relative wind”. Oncoming relative is wind is of course talking about the direction we are traveling. Its really an overcomplication that makes it sound so much more scary than it actually is, and its pretty much saying, oncoming wind is directly opposite to our direction of travel(velocity vector). The angle between the chordline(direction the nose points for simplicity), and the velocity vector is our angle of attack(on the pitch axis). In my AoA calculation, im just isolating that vertical pitch axis, and finding the angle between our velocity vector, and our lookvector(look vector is the direction our nose is pointing).

Used your lift equation and it works! But I’m afraid that it is not done, because I’ve moved onto the next problem: drag. I am concerned being unable to observe the 100% functionality of the lift as the drag turns out a warden.

Again, I used @Imperiector’s formula, which was obscure to me. At first, I thought it had something to do with liftPower, in which is baked into lift-induced drag. I removed that, and the problem occurs to be more frequent.

The drag force, the parallel of thrust, somewhat goes to tremendously large numbers. It seemed fine in the first 2-3 seconds, but it went the other way around after.

local CD = 0.01 -- Drag coefficient
local S = 2 -- "Any number"
local wingAR = 3 -- ¯\_(ツ)_/¯
local oswald = 0.7 -- "Generally less than 1"

local Q = airDensity * 0.5 * velocity.Magnitude^2 -- Dynamic pressure

function calculateDrag(pressure, CL, AoA)	
	local CLAoA = CL * AoA
	local CD_i = CLAoA^2 / (math.pi * wingAR * oswald) -- Lift-induced drag
	local drag = (CD * CD_i) * pressure * S
	
	return drag
end

I would like for more assistance from @Imperiector, as I am unsure if either the formula was wrong, or something else.

Besides, the lift is currently perfect. Thank you for the correction @xPoIarity.

Your velocity is a constant number which is the issue.

For videogame purposes you can ignore all other constants other than velocity and end up with this, a simple model where a plane accelerates until it reaches terminal velocity.

This basically makes the assumption that your plane is a flying sphere with constant area, and assumes the air density is constant. You can ignore @Imperiector realism for this.

Ultimately all it does is it will prevent a plane from infinitely accelerating and allow the plane to deaccelerate.

To make a simple plane model from the above I would add angular velocity to turn the plane with roll that is inversely proportional to speed. (More speed harder to turn)

And an anti gravity forces that scales to body weight to simulate lift once it reaches a certain speed.

I’m certain that I’ve made a drag simulation with a fixed coefficient and it works fine.

I tried using your method, it works, but I can’t seem to adjust the speed limit. I tried putting a * CD, it caused infinite drag power. While being a simple formula, it is less realistic without Li drag.

No, it’s ran in a loop to keep it relative to time. Didn’t write like that.

Update: I don’t know what I’ve done with the modifications. I don’t know which formula to rely on, and now the lift itself is printing out huge values. I don’t know what’s causing this, lift, drag. Could be both. I’m lost right now.

function calculateLift(velocityMag, AoA)
	AoA = math.deg(AoA)
	local CL = math.clamp(math.sin(math.rad(3 * AoA)), -60, 60)
	
	return 0.5 * velocityMag^2 * CL * liftPower, CL
end

function calculateDrag(velocity, pressure, CL, AoA)
	local CLAoA = CL * AoA
	local CD_i = CLAoA^2 / (math.pi * wingAR * oswald)
	
	return (CD + CD_i) * pressure * S
end
local cF = pt.CFrame
local velocity = pt.AssemblyLinearVelocity
local velMag = velocity.Magnitude

if velocity.Magnitude == 0 then velMag = 0.001 end -- Prevent nan
	
local localVelocity = cF:VectorToObjectSpace(velocity)
	
local AoA = math.atan2(-localVelocity.Y, -localVelocity.Z)
	
local windex = cF * CFrame.Angles(-AoA, 0, 0) * CFrame.Angles(0, -AoS, 0)
	
local dir_lift = windex.UpVector
local dir_side = windex.RightVector
local dir_drag = -windex.LookVector
	
local pressure = airDensity * 0.5 * velMag^2
	
local lift, CL = calculateLift(velMag, AoA)
local drag = calculateDrag(velocity, pressure, CL, AoA)

pt.Lift.Force = dir_lift * lift
pt.Drag.Force = dir_drag * drag

My sticks collapsed.

Drag force is not opposite to thrust, its actually opposite to velocity(your direction of travel). The reason your lift is outputting huge values is most likely because your drag force is massive. This is most likely because your drag force is turning around, and facing the direction your moving, which means that it will propel you forward as you move, which the calculation would then add in(since we are using velocity^2) which will increase the same drag thats propelling your forward. Its a feedback loop that happens when your drag force isn’t facing the right way, causing your lift to become huge as well, since it incorportates v^2. Here’s my drag equation:

function Physics.calculateDrag(velocity, dragPower, altitude)
	if velocity.Magnitude == 0 then return Vector3.zero end -- Dont return NaN drag

	-- Drag = 0.5 * velocity^2 * airdensity * dragCoefficient 
	local dragCoefficient = dragPower * velocity.Magnitude
	local airDensity = 1.2 * math.exp(-altitude / 25000) -- Simplified exponential decay
	local dragForce = 0.5 * velocity.Magnitude^2 * airDensity * dragCoefficient

	return dragForce/1000
end

Similar thing again, its just a simplified version of the realistic drag equation. You’ll see how im just considering our velocity.magnitude twice, once in the drag equation, and once in the coefficient. The coefficient of drag isn’t something thats fully understood in aviation, but its this extra variable thats measured in flight tests. In your simulation, its just a way to apply your drag power tuning variable until it feels right. You can actually use AoA to calculate the drag coefficient, which is slightly more realistic, and i actually did a while ago, but i gave that up for just a tuning variable(it was a bit problematic and messed with the drag direction). Again, our drag equation is derived from the real equation, but simplified. We ignore wing area, replaced with our dragPower tuning variable, increased to account for the drag coefficient(I found 0.001 to be good assuming you multiply it by velocity magnitude). Just dont forget to apply in the direction OPPOSITE TO VELOCITY(AssemblyLinearVelocity).

self.drag.Force    = drag * -velocity.Unit -- The Drag force is opposite to Velocity

Also, check your AoA calculation.


Try getting a part to represent the direction of your velocity, and the direction of your nose to see if the AoA output is making sense. Your incorporting two axes (pitch AND yaw axes) into your AoA value, which might ruin things. It could also cause massive lift values if its wrong. Here’s mine, it works properly as far as i know:

    -- Oncoming wind is opposite to the direction of the velocity vector
	-- The AOA is calculated by getting the angle that the look vector deviates from the velocity vector
	if velocity.Magnitude > 0 then 
		-- Project the velocity vector onto the plane's vertical plane
		local projectedVelocity = velocity.Unit - CF.RightVector * velocity.Unit:Dot(CF.RightVector)

		-- Calculate the angle between the projected velocity and look vector
		local dotProduct = math.clamp(CF.LookVector:Dot(projectedVelocity.Unit), -1, 1)
		local cross = CF.LookVector:Cross(projectedVelocity.Unit)
		local sign = cross:Dot(CF.RightVector) > 0 and -1 or 1 -- Check if the aoa is pos/neg, if nose is above/below velocity vector

		-- Convert to degrees and apply neg or pos, clamp from -90 to 90 
		AOA = math.clamp(math.deg(math.acos(dotProduct)) * sign, -90, 90)
	else AOA = 0 end -- To avoid NaN AoAs

We are isolating the pitch axis from the yaw axis, and measuring the angle between. At the end, we are clamping to 90 and -90 AoA, and checking whether the plane’s look vector is below/above its direction of travel to see if its negative or positive AoA. Positive AoA is when the nose is above the Velocity Vector. Negative AoA is when the nose is below the Velocity vector. This way, our lift force will output NEGATIVE lift when we are pointing the nose below vel vec, and positive lift when we are pointing the nose above.

Also, your calculating a pressure variable which does not make sense. If your looking to incorporate some things @Imperiector is mentioning, make sure to get your basic forces working first so you dont get weird behavior that you cant explain. After that, you can add all the extra behavior you want.

I believe that windex.dir_drag is a modified version of -velocity.Unit that involves AoA.

Checked the AoA again, and it was the issue. After a takeoff, it repeatedly switched from positive to negative, which explained why it looked wobbly up-down before getting nullified.

I switched to your drag formula. It’s printing out huge numbers without the part even moving.

I went back to AoA calculation, which also uses your method, it does the same behavior. I notice that the wobble effect started from minuscule decimals, which gradually scales up and causes the large numbers.

Windex is using the cframe to find the relative airflow from the pitch AoA and Angle of Sideslip so you can apply drag in the opposite direction. Yes its a modified version, but it shouldn’t be causing the problem. If its happening regardless of the method you use to calculate AoA, that means something else is causing the weird AoA. What are you using as your plane? Is it a single mesh, or a rig with many parts? Also, how are you handling gravity? If you can, share a video of the wobble behavior.

I use a frictionless WedgePart, and my jet model with wheels and suspensions installed. Server-sided. This is how I set the constraints up for both models:

Screenshot 2025-03-08 132248

Thrust is relative to Attachment, while Drag and Lift to world. AlignOrientation is for testing AoA, and just how airplanes pitch up to lift.

WedgePart


Last time, it works for the first few seconds, where it manages to takeoff, then wobbles and disappear after. The values outputted large numbers. I used @Imperiector’s drag formula which worked for me.

Today, I gave another try with both drag models. Both WedgeParts disintegrated even without thrust. As how I remember Roblox physics is, there’s like a tiny amount of velocity on unanchored parts when the simulation starts.

I tried to print out both drag and lift value to check for large numbers. What’s weird is, there is no large numbers, other than decimals and decimals. It’s last print output when the WedgePart disappears is 0.

There was a Roblox update recently, and that’s when it started to happen even with @Imperiector’s drag formula. This is definitely something to do with their engine.

I cannot share you a clip of it’s wobbly motion, as the WedgePart itself disappears into thin air before doing anything.

Rigged Jet


Jet is pitched upwards 5 degrees.

Not far different, except it doesn’t immediately fling with @Imperiector’s formula.

I’ve tried with both suspensions enabled and disabled, they react the same way.

The worst of all, I did barely any modifications to the code and when I started to clip them, they don’t seem to react anymore. It’s my horrible part of programming when it comes to not knowing the root of an issue.

Latest version of my script:

local pt = script.Parent

local airDensity = 1.2
local liftPower = 100

local CD = 0.01
local S = 2 -- ¯\_(ツ)_/¯
local wingAR = 3
local oswald = 0.7

function calculateLift(velocity: Vector3, AoA)
	
	AoA = math.deg(AoA)
	print(AoA)
	local CL = math.clamp(math.sin(math.rad(3 * AoA)), -60, 60)
	
	return 0.5 * velocity.Magnitude^2 * CL * liftPower, CL
end

function calculateDrag(velocity: Vector3, pressure, CL, AoA)
	
	local CLAoA = CL * AoA
	local CD_i = CLAoA^2 / (3.14 * wingAR * oswald)
	
	return (CD + CD_i) * pressure * S
end

function calculateAOA(velocity, CF)
	local AoA
	if velocity.Magnitude > 0 then 
		-- Project the velocity vector onto the plane's vertical plane
		local projectedVelocity = velocity.Unit - CF.RightVector * velocity.Unit:Dot(CF.RightVector)

		-- Calculate the angle between the projected velocity and look vector
		local dotProduct = math.clamp(CF.LookVector:Dot(projectedVelocity.Unit), -1, 1)
		local cross = CF.LookVector:Cross(projectedVelocity.Unit)
		local sign = cross:Dot(CF.RightVector) > 0 and -1 or 1 -- Check if the aoa is pos/neg, if nose is above/below velocity vector

		-- Convert to degrees and apply neg or pos, clamp from -90 to 90 
		AoA = math.clamp(math.deg(math.acos(dotProduct)) * sign, -90, 90)
	else AoA = 0 end -- To avoid NaN AoAs
	return math.rad(AoA)
end


game["Run Service"].Heartbeat:Connect(function(dT)
	
	local cF = pt.CFrame
	local velocity = pt.AssemblyLinearVelocity
	if velocity.Magnitude == 0 then velocity = Vector3.new(0, 0.001, 0) end
	
	local localVelocity = cF:VectorToObjectSpace(velocity)
	
	local AoA = calculateAOA(velocity, cF)--math.atan2(-localVelocity.Y, -localVelocity.Z)
	local AoS = math.asin(localVelocity.X / localVelocity.Magnitude)
	
	local windex = cF * CFrame.Angles(-AoA, 0, 0) * CFrame.Angles(0, -AoS, 0)
	
	local dir_lift = windex.UpVector
	local dir_side = windex.RightVector
	local dir_drag = -windex.LookVector
	
	local pressure = airDensity * 0.5 * velocity.Magnitude^2
	
	local lift, CL = calculateLift(velocity, AoA)
	local drag = calculateDrag(localVelocity, pressure, CL, AoA)
	
	pt.Lift.Force = dir_lift * lift
	pt.Drag.Force = dir_drag * drag
	
end)

I am unable to determine what really causes the issue. But if I really have to, I’m saying it’s the Roblox engine itself.

Dont use align orientation. If your using constraints to get your plane to rotate, use angularVelocity. Handle inputs 60 times a second, and set the target angular velocity to be the current input on pitch/yaw:

function Controls.handleControls(flightControl, plane, stats, gun, velocity, AOA, deltaTime)
	local rollInput  = 0
	local pitchInput = 0
	local yawInput   = 0
	local degToRad = math.pi/180

	-- Process control inputs
	if UIS:IsKeyDown(Enum.KeyCode.LeftShift)   then throttle = math.min(throttle + 0.0025, 1) end
	if UIS:IsKeyDown(Enum.KeyCode.LeftControl) then throttle = math.max(throttle - 0.0025, 0) end
	if UIS:IsKeyDown(Enum.KeyCode.W) then pitchInput = -stats.TurnRate * 0.5 end
	if UIS:IsKeyDown(Enum.KeyCode.S) then pitchInput = stats.TurnRate  end 
	if UIS:IsKeyDown(Enum.KeyCode.A) then rollInput  = stats.RollRate  end
	if UIS:IsKeyDown(Enum.KeyCode.D) then rollInput  = -stats.RollRate end
	if UIS:IsKeyDown(Enum.KeyCode.Q) then yawInput   = stats.YawRate   end
	if UIS:IsKeyDown(Enum.KeyCode.E) then yawInput   = -stats.YawRate  end

	rollInput = rollInput + (yawInput * 1.75) -- Roll when the plane yaws
	local angularVelocity = Vector3.new(pitchInput * degToRad, yawInput * degToRad, rollInput * degToRad) -- Calculate base angular vel

	flightControl.AngularVelocity = angularVelocity
end

The max torque you set on the AngularVelocity constraint will be how quick the plane responds to rotational input. Align orientation is a weird way to handle it, kind of like setting cframe directly. Also, set thrust relative to WORLD

To mention that I did these server-sided. I thought it had something to do with the wobble behavior. I decided to try it on my client, and that’s when I couldn’t find out.

The jet doesn’t want to get past the speed of lifting off. I adjusted the liftPower, and the top speed is capped at when it tries to takeoff, noticed by a wobbly behavior.

It’s just a copy-pasted script, yet it works differently. This does not make sense.

If your plane cant take off, its because the gravity is too strong. That, or the forces are too weak relative to the mass of the plane they are effecting. The code im giving you relies on the mesh being the proper mass(you can change this by changing the density), proper game gravity(0 since i use a vector force to handle gravity separately), and a bit of fiddling with inertia. Roblox vector forces aren’t really proportional, so you have to manipulate the velocity vector directly to nudge it towards where the thrust is pointing. Heres what i did for that:

function Physics.alignVV(velocity, lookVector, throttle, plane, dt)
	local velocityUnit = velocity.Unit
	local lookVectorUnit = lookVector.Unit

	local dotProduct = velocityUnit:Dot(lookVectorUnit)
	local angleBetween = math.acos(math.clamp(dotProduct, -1, 1)) 

	-- Scale correction angle with delta time to decouple from frame rate
	local correctionAngle = math.clamp(0.1 + (throttle * (0.5 - 0.01)), 0.01, 0.5) * (dt/(1/60)) 
	local correctionMagnitude = math.rad(correctionAngle)  

	-- If the angle is greater than the threshold, do the correction
	if angleBetween > correctionMagnitude then
		local correctionAxis = velocityUnit:Cross(lookVectorUnit)  
		local correctionDirection = correctionAxis.Unit * correctionMagnitude

		-- Gradually change the velocity direction using the correction.
		local correctedVelocity = velocityUnit:Lerp(lookVectorUnit, correctionMagnitude)  
		correctedVelocity = correctedVelocity * velocity.Magnitude
		plane.AssemblyLinearVelocity = correctedVelocity 
	end
end

It just directly changes the direction of the velocity vector based on the current thrust, and interpolates to make it smooth.

Use airfoils, search them up there great they simulate lift and drag for a part it custom airfoil characteristics that’s the most realistic physics your gonna get, i only know 3 utilize airfoil physics, hostile skies, airx 2020 and Novus flight simulator

I’ve re-examined every calculation made. From AoA, coefficients, and stuff. They all make sense, except one thing: lift.

The fling behavior is caused by the lift force. As I said before, it increases and decreases repeatedly from little decimals to large numbers. The liftCoefficient is fine, so I doubt it’s something to do with the airspeed/velocity.

I use .Heartbeat to run the physics. The RunService constantly updates the force. I came up with the idea, that the latency between the updates causes the velocity to be not perfect, hence why the wobble behavior started from tiny decimals. However, that might not be the case.

That’s the server side. On the client side, I would say that it already tries to fling itself at low speeds, cancelled by the solid surface below, and declining it from exceeding said speed.

Knowing that the lift is the problem here, I thought of using the zero gravity formula, by multiplying .AssemblyMass with .Gravity, but this time is affected by my desired stallSpeed. It didn’t go well, and is cancelled.


Other than lift, there’s still issue with drag. If it the part reaches zero velocity, it becomes non-existent. That’s when they check if it’s zero, it would be replaced with something like 0.001. My jet model has suspensions on, preventing this from happening since the velocity never reaches zero.

What do I do?