Car Suspension (Scripted)

can you post the place file here so I can test some different things in it?

SuspensionVehicle.rbxm (15.5 KB)
here’s file with all the scripts

3 Likes

might be an issue on my end, but I can’t download it. Can you post a link to an uncopylocked Game?

Solution to the vehicle is that it might have a bodygyro and that might cause it!

Hi i have made a suspension car and i dont know if i can reduce the amount of recources that it uses as my game will have driving NPCs that use the vehicles that you can steal from them.
I need at least 50 vehicles and i struggle to get this done with the limits of roblox
the activity idles around 1% and that is not good for the rate. Any help will be taken
code down below.

local RS = game:GetService("RunService")
local VehicleSeat = script.Parent.Body.DriveSeat
--Vehicle controller
VehicleSeat.Anchored = true
local CenterPart = Instance.new("Part")
CenterPart.Size = Vector3.new(1,1,1)
CenterPart.CFrame = script.Parent:GetModelCFrame()
CenterPart.Parent = script.Parent
CenterPart.Name = "CenterPart"
local Weld = Instance.new("WeldConstraint")
Weld.Parent = CenterPart
Weld.Part0 = CenterPart
Weld.Part1 = VehicleSeat
VehicleSeat.Anchored = false
Mass = 0
Vehicle = script.Parent
for i,x in pairs(Vehicle:GetDescendants()) do
	if x:IsA("BasePart") then
		Mass = Mass + x:GetMass()*game.Workspace.Gravity
	end
end
-- Settings
Bounce = 200
Height = 4
Suspension = 4
Traction = 10000000
for i,x in pairs(Vehicle.Edges:GetChildren()) do
	local Weld = Instance.new("Motor6D")
	Weld.Parent = x.Wheel
	Weld.Name = "SuspensionWeld"
	Weld.Part0 = x.Wheel
	Weld.Part1 = x
end
--
force = Mass * Suspension
damping = force / Bounce
--
while true do
	game:GetService("RunService").Stepped:wait()
	--VehicleSeat.Velocity = VehicleSeat.Velocity*0.999*(1-0.01)
	for a,Thruster in pairs(Vehicle.Edges:GetChildren()) do
		if Thruster.Wheel:FindFirstChild("SuspensionWeld") ~= nil then
			local bodyThrust = Thruster.BodyThrust
			local hit, position = game.Workspace:FindPartOnRayWithIgnoreList(Ray.new(Thruster.Position, Thruster.CFrame:vectorToWorldSpace(Vector3.new(0, -1, 0)) * Height),{script.Parent})
			local thrusterHeight = (position - Thruster.Position).magnitude
			if hit then
				--If we're on the ground, apply some forces to push the wheel up
				bodyThrust.force = Vector3.new(0, ((Height - thrusterHeight)^2) * (force / Height^2), 0)
				local thrusterDamping = Thruster.CFrame:toObjectSpace(CFrame.new(Thruster.Velocity + Thruster.Position)).p * damping
				bodyThrust.force = bodyThrust.force - Vector3.new(0, thrusterDamping.Y, 0)
			else
				bodyThrust.force = Vector3.new(0, 0, 0)
			end
			Thruster.Wheel.SuspensionWeld.C0 = CFrame.new(0.5,math.min(thrusterHeight,Height * 0.8) - (Thruster.Wheel.Size.Y / 2), 0)*CFrame.Angles(0,math.rad(180),0)
			Thruster.Traction.MaxForce = Vector3.new(Traction,0,Traction)
			Thruster.Traction.Velocity = Vector3.new()
		end
	end
end

It works with no issues, just it causes alot of activity. You can use this code if you so want to!

4 Likes

I would still recommend using an existing chassis from another player if possible - it took me about a month to get a basic car chassis that works reliably and with decent handling from scratch, and many more to get a chassis that was release worthy, and that’s with a lot of background in both Roblox and physics.

If you’re still quite new to Roblox it’s quite a big leap in difficulty and may be more effort than it’s worth if cars aren’t the number 1 focus of the game.

1 Like

I myself am current revisiting this whole raycasting chassis topic again, and this post too- I’ve been looking over this post since about this time last year and I was surprised to find it’s somehow active again?

The thing I’ve been struggling with is actually damping the springs- I just don’t get the formula. If I could get that down, I could easily make the rest of the chassis with no issue, so it sucks that one thing is holding me back.
I think the best I can do for now is looking over all these new resources and study how they apply damping.

1 Like

What part of it is causing problems? Just getting the equation to work at all, or tuning it correctly?

1 Like

If you want, I got a model that uses scripted suspension and it uses around 0.5% - 2% of activity

I hope this helps with your work!

1 Like

Getting the equation to work- I understand F = ke perfectly fine, but as soon as I search for things such as “spring damping formula”, I can never understand any of it.

1 Like

Damping is proportional to rate of compression of the spring, so for example in a case where one end is fixed and one end is moving at 1m/s, F = 1C where C is the damping constant. If both ends are moving towards each other at 1m/s each, then F = 2C.

For the cars, the length of the spring, X is determined each frame by the raycast. The rate of compression is therefore equal to (Xcurrent - Xlast_frame) / (time_since_last_frame)

2 Likes

So from understanding what you’ve said, the damping force is equal to the damping constant, multiplied by the rate of compression? Now that’s starting to sound like something I can manage with!

I’ll give this a go right now!

EDIT:
Am I also right in thinking that I would then just take away the damping force from the original force calculate through f = kx?

4 Likes

Yep, you’ve got that right!
f = kx - cẋ

Where ẋ the rate of compression given above.

2 Likes

I’d just like to say, thank you SO much for this- I cannot express how ecstatic I am that I FINALLY have my own damped spring ACTUALLY working. I was considering contacting you for a long time in regards to this, but I wasn’t sure if I should.

Obviously, this is fundamentally working flawlessly, which really pleases me!
However, I am getting one odd and annoying issue- the part that moves like the bottom of the spring would, just… vanish?

After it comes to rest, a short while later it just completely disappears- no weird graphical issues, no errors, nothing, it just deletes itself from the workspace, however, I assume the script is still running, as it’s still there, but surely it should error if the part disappears? I have set up some debugging tests by continuously running print(Bottom) (Bottom being the reference to Model.Bottom - the part), and it continuously just prints Bottom as I would expect, even when deleted- however, when it gets deleted while I’m running print(Bottom.Parent) it DOES detect that it’s now nil!

This is genuinely perplexing me, as it simply reminds me of the half-life of a radioactive element- the part just disappears at a random time! At first I thought it was due to a stiffness that is too high (800 stiffness, a 100 damping constant), but that’s not the case either! For reference, I am now using RunService:HeartBeat, instead of while true do wait(), as this allows me to retrieve “step” as an argument, which gives me the duration since last frame.

I appologise that this is a VERY lengthy response, but this is just so out of the blue, that I have not been able to fix it.

4 Likes

Have you got a divide by zero error somewhere? I was perplexed for ages about a bug that made parts of my car vanish, would make level geometry disappear, would make the car stop existing for a few frames, and even freaking warp back in time. Turned out one of my equations was very occasionally dividing by zero and putting this into the welds and forces, and for some unknown reason, instead of throwing an error or just doing nothing, Roblox just has completely unhandled and unpredictable behavior.

Similar issues also arise if you try to use .Unit on a 0,0,0 vector.

1 Like

Also, something else that may come up later:

When simulating a stiff spring with discrete time steps, you can end up in a positive feedback loop. E.g., the spring deflects, which causes a correcting force, but by the time the code runs again the spring is now deflected in the opposite direction, causing a larger correcting force, causing it to deflect even more in the opposite direction…

The full explanation is given here (https://www.gamedev.net/articles/programming/math-and-physics/towards-a-simpler-stiffer-and-more-stable-spring-r3227/)

But the tl’dr is you need to limit the force of the suspension.
The force due to the deflection should be limited to A * (mass / NumWheels) / (dt ^ 2)
Where A is a constant less than 0.5 which you’re best of just finding by trial and error to see what works, and mass is the total mass of the vehicle.
And the force due to damping should be limited to * (mass / NumWheels) / (dt)

6 Likes

Well I’ve had a look and the only place where I actually divide is when I’m calculating the compression rate.
My script is very basic and simply is just, at the minute, one part that springs around another part on the y axis, just so I can get the formula and script correct.

Due to using RunService.Heartbeat:Connect and passing through “stepDuration” as the time since last heartbeat, and considering it is a very small number like 0.01, I believe that is why it’s doing that.

New revelation, while writing this, I was just inspecting the output, where I outputted the duration, just before it disappears, it DOES show that 0 came up as the duration, so you were completely right!

In that case, what method would I use to constantly calculate this while also retrieving the duration since last frame?

EDIT:
I tried using while true do wait(0.05), but obviously if I then divide by 0.05 as the duration, that isn’t the time since the last frame and is instead the time since the last loop!

2 Likes

I’m not sure why RunService.Heartbeat would be passing 0 to your function, but if it is than that would very neatly explain the problem. You could always measure the difference in elapsedTime() to get a value for the time passed instead

As for your edit, dont worry about that. Sorry if my language was confusing, time since last loop is what it should be and is probably a more accurate term :slight_smile:

Ah so in the case I explained above, I could just pass 0.05 as that is time since last loop? Earlier it broke, but I’ll give it another go- maybe it was just me messing up my code.

Nevertheless, thank you for your contribution so far, I can see why you have Top Contributor right next to your name!

So now I have a while true do loop, each time retrieving the result of the elapsedtime() from last loop and taking that away from the elapsedtime() that is ran during the current loop, but now it just seems very inaccurate compared to before. Although the vanishing issue is fixed, now the stiffness and damping constant values just don’t work like they used to!

A stiffness of, say 800 and damping constant of 30 would be a very sharp, but damped properly oscillation. Now it just rapidly jumps up and down and disappears, just like it would if I didn’t even factor in dampening!

This feels like 1 step forward, 2 steps back :frowning:
I’ll keep at it though, perhaps I’m not using the right method for measuring the time since last iteration

1 Like

Small heads up, I believe I’ve managed to fix this- I reverted back to using RunService.Heartbeat and instead just made an if statement that detects if the time since last frame is returned with zero and replaces that with the previously returned value, meaning, theoretically, the value shouldn’t never reach zero, meaning I should never divide by zero- very good results so far, it hasn’t vanished at all!

Looking back at the history of this post- it’s essentially a raycasting-chassis megathread at this point!

3 Likes