Help with hovercraft physics

You can write your topic however you want, but you need to answer these questions:
So basically I’m trying to make a hover craft, however i can not seem to get the physics down whatsoever.

Ive made 4 attempts each one building off the last after the 1st one

Here are the results that i recorded not in any particular order

function ptm() -- Ptm = PartMaker
	local Part ="Part")
	Part.Anchored = true
	Part.CanCollide = false
	Part.Size =,1,1)
	Part.Parent = script.Parent
	return Part
local Run = game:GetService("RunService")
local Thrusters = {}
for i, v in pairs(script.Parent:GetChildren()) do
		if v:IsA"Part" then
			if v ~= script.Parent.PrimaryPart then
--					while wait() do
--					end
				table.insert(Thrusters, v)
				local BodyVelocity ="BodyVelocity")
				BodyVelocity.Parent = v
				BodyVelocity.MaxForce =,math.huge,math.huge)
				BodyVelocity.P = 10
					while wait() do
						--BodyVelocity.Velocity = v.CFrame.LookVector * 0
for i, v in pairs(Thrusters) do
--		local Part = ptm()
--		local Part2 = ptm()
--		local Part3 = ptm()
		local BodyVeloicty ="BodyVelocity")
		BodyVeloicty.Parent = v
		BodyVeloicty.MaxForce =, math.huge, math.huge)
		local Dist
			while true do
				print(v.Name, BodyVeloicty.Velocity)
				print(math.ceil(Dist), math.floor(Dist), Dist)
			local Mag = 500
			local Power = script.Power.Value
			local Velocity = script.Velocity.Value
			BodyVeloicty.P = Power
			local A = v.Position -,1,0)
			local B = v.Position
			local ray =, B + (A - B).Unit * Mag )
			local part, position = workspace:FindPartOnRayWithIgnoreList(ray, script.Parent:GetChildren())
			if part ~= nil then
					Part.CFrame =
					Part2.CFrame =  +,2,0))
					Part3.Color = Color3.fromRGB(0,0,0)
					Part3.CFrame = + position)/2)
					Part3.Size =,(v.Position - position).Magnitude,.1)
				Dist = (position - v.Position).Magnitude
				if Dist <= 5 then
					BodyVeloicty.Velocity = v.CFrame.UpVector * Velocity
				elseif Dist > 5 then
					BodyVeloicty.Velocity = v.CFrame.UpVector * -1

You should factor in the SurfaceNormal returned by raycasting in your velocity caclulations. You should also not do an if statement like that with the Dist, you should let the velocity slowly taper off when it gets farther away. I’d reccomend adding BodyGyro so you can have control of the vehicle’s rotation. If you’re going to be adding the ability to control it, then you should use AngularVelocity for steering and obviously the BodyVelocity for forward motion.

Also, I would reccomend having one Stepped connection for doing all the calculations.

I would go with what @EDMaster24 said. Also the force applied should follow the Inverse Square Law if you want to stay accurate to physics.

Here’s how I achieve cool looking hover physics:

Here’s an equation of how force would work this way:
force = thrust - thrust/((distanceToFloor/hoverHeight)^2) + workspace.Gravity*totalMass

You should also disable the force if the distanceToFloor is greater than or equal to twice the hoverHeight or less than 0 (which should never happen but could possibly happen).

If you think about it like this if the distance to the floor is 10 and your hover height is 10 you should get a force of thrust - thrust/1^2 or thrust - thrust (0). If you get too close to the floor the thrust will increase more and more the closer you get. If you get far away the thrust will decrease since it can’t reach that far.

To determine the right force you can use something like this:
We know that in Roblox to get a zero gravity effect using force you’d apply a y force of workspace.Gravity*totalMass
So our force can be twice this “anti gravity” force meaning at 20 studs (twice the hover height) you’ll achieve exactly workspace.Gravity*totalMass of force downwards (normal falling)
At 0 studs from the floor you’ll essentially be falling upwards.

The thrust value can be any value and it will determine the stability and speed of hovering. workspace.Gravity*totalMass is one times gravity. Since your dist/hoverHeight multiplier can reach 2 maximum (2^2 is 4) you need to at least divide the anti gravity force by 4 to make sure the hovercraft does not continue to bounce higher and higher. This doesn’t look all that smooth though so you can multiply this 1/4th gravity force by a decimal and play around with the results. 0.25 would be 25% this value for example.

Fun fact: This also works with multiple “thruster” parts and you can use BallJoints to connect them to the main body and BodyGyros to orient them upwards!


what would thrust be in this case

I explain that below (the second to last paragraph)

I also just noticed something there that I need to fix. Rather than two times the anti gravity force it can be any force since the antigravity force is added to the force equation already.

1 Like

well what i was doing before u came was this

v.VelocityValue.Value = MaxDist/(Dist)
local Velocity = math.floor(v.VelocityValue.Value) * 10
Dist = (position - v.Position).Magnitude
BodyVeloicty.Velocity = v.CFrame.UpVector * math.floor(MaxDist/(Dist)) * 10

So if the craft got further away it would increase velocity and viceversa but here was the result

I am using raycasting to determine the distance to the floor and the angle of thrust. I can upload my place file with the hovering method I’m talking about. I’ve also updated my original post to include the fixed information.

Watch Screen Recording 2019-09-18 at 12.30.17 AM | Streamable the result

Here’s a place with the hover car I made in it: Hover Car Test - Roblox

It’s uncopylocked as well. It’s a bit of an old script so I apologize for it being a little messy.

Ty anyways but this was what i was after the rotation for yours isnt bad but something about it seems off

kinda like this Watch Screen Recording 2019-09-18 at 1.40.40 AM | Streamable

That looks cool! Good job :smile:

Not mine that is the desired goal

My bad. It actually looks like the hovercraft in that video doesn’t use realistic thrusting. It looks like it’s simply using a BodyPosition and moving itself to the target height from the surface beneath. It also looks like it’s using a BodyGyro to rotate to the surface normal of the object directly beneath it. It looks like it may just be using the averages of the surface normals under the corners.

The target position should be the averaged surface normal times the target height. The target cframe is just a rotated cframe with the surface normal as the unrotated cframe’s look vector.

1 Like

Use a BodyForce Thruster as your primary Force Thruster. Your system can then be modeled via state space as x_dot = Ax + Bu. State x is can be simplified as f=ma; thus yielding matrix A = {{0, 1}, {0, 0}} as velocity/acceleration are not dependent on state. B matrix is {{0}, {1/m}} due f/m = a.

With your state-space model, use LQR or Pole Placement to calculate gain matrix K. Gain matrix K can then be used in control law u = r-Kx. If you want, you can also implement a Kalman filter, but I think 20hz readings from your Raycasted position sensor should be suitable enough… Perhaps velocity measurement could be improved though as phase-locked velocity loops really suck.

Feed foward (u_final = u + mg) can be implemented without model consideration, as the model assumes the system has no external forces acting upon it. Even without feed foward, properly chosen gain matrix K should yield good reference tracking.