Help with stabilizing Scripted Suspension

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!

I want to help stabilize my Scripted Suspension car. Keep it planted down on the ground when turning or going over terrain but so it can still go in the air

  1. What is the issue? Include screenshots / videos if possible!

Im not sure how to do this i am using modified version of the Jeep Suspension.

What i want is. But dont know how to make

What I have right now:

  1. What solutions have you tried so far? Did you look for solutions on the Developer Hub?

After that, you should include more details if you have any. Try to make your topic as descriptive as possible, so that it’s easier for people to help you!

CarScript “Script”.

local car = script.Parent
local stats = car.Configurations
local Raycast = require(script.RaycastModule)

local Mass = 0

for i, v in pairs(car:GetChildren()) do
	if v:IsA("BasePart") then
		Mass = Mass + (v:GetMass() * 196.2)
	end
end

local bodyPosition = car.Engine.BodyPosition
local bodyGyro = car.Engine.BodyGyro

--local bodyPosition = Instance.new("BodyPosition", car.Engine)
--bodyPosition.MaxForce = Vector3.new()
--local bodyGyro = Instance.new("BodyGyro", car.Engine)
--bodyGyro.MaxTorque = Vector3.new()

local function UpdateThrust(Thrust)
	-- Raycasting
	local hit, position = Raycast.new(Thrust.Position, Thrust.CFrame:vectorToWorldSpace(Vector3.new(0, -1, 0)) * stats.Height.Value) --game.Workspace:FindPartOnRay(ray, car)
	local ThrustHeight = (position - Thrust.Position).magnitude

	-- Wheel
	local Weld = Thrust:FindFirstChild("Weld")
	Weld.C0 = CFrame.new(0, -math.min(ThrustHeight, stats.Height.Value * 0.8) + (Weld.Part1.Size.Y / 2), 0)
	-- Wheel turning
	local offset = car.Engine.CFrame:inverse() * Thrust.CFrame
	local speed = car.Engine.CFrame:vectorToObjectSpace(car.Engine.Velocity)
	if offset.Z < 0 then
		local direction = 1
		if speed.Z > 0 then
			direction = -1
		end
		Weld.C0 = Weld.C0 * CFrame.Angles(0, (car.Engine.RotVelocity.Y / 2) * direction, 0)
	end
end

car.DriveSeat.Changed:connect(function(property)
	if property == "Occupant" then
		if car.DriveSeat.Occupant then
			local player = game.Players:GetPlayerFromCharacter(car.DriveSeat.Occupant.Parent)
			if player then
				car.DriveSeat:SetNetworkOwner(player)
				local localCarScript = script.LocalCarScript:Clone()
				localCarScript.Parent = player.PlayerGui
				localCarScript.Car.Value = car
				localCarScript.Disabled = false
			end
		end
	end
end)

--spawn(function()
while true do
	game:GetService("RunService").Stepped:wait()
	for i, part in pairs(car:GetChildren()) do
		if part.Name == "Thrust" then
			UpdateThrust(part)
		end
	end
	if car.DriveSeat.Occupant then
		bodyPosition.MaxForce = Vector3.new()
		bodyGyro.MaxTorque = Vector3.new()
	else
		local hit, position, normal = Raycast.new(car.Engine.Position, car.Engine.CFrame:vectorToWorldSpace(Vector3.new(0, -1, 0)) * stats.Height.Value)
		if hit and hit.CanCollide then
			bodyPosition.MaxForce = Vector3.new(Mass / 5, math.huge, Mass / 5)
			bodyPosition.Position = (CFrame.new(position, position + normal) * CFrame.new(0, 0, -stats.Height.Value + 0.5)).p
			bodyGyro.MaxTorque = Vector3.new(math.huge, 0, math.huge)
			bodyGyro.CFrame = CFrame.new(position, position + normal) * CFrame.Angles(-math.pi/2, 0, 0)
		else
			bodyPosition.MaxForce = Vector3.new()
			bodyGyro.MaxTorque = Vector3.new()
		end
	end
end
--end)

LocalCarScript “LocalScript”

--local camera = game.Workspace.CurrentCamera
local player = game.Players.LocalPlayer
local character = player.Character
local humanoidRootPart = character.HumanoidRootPart
local car = script:WaitForChild("Car").Value
local stats = car:WaitForChild("Configurations")
local Raycast = require(car.CarScript.RaycastModule)

--local cameraType = Enum.CameraType.Follow

local movement = Vector2.new()
local gamepadDeadzone = 0.14

car.DriveSeat.Changed:connect(function(property)
	if property == "Steer" then
		movement = Vector2.new(car.DriveSeat.Steer, movement.Y)
	elseif property == "Throttle" then
		movement = Vector2.new(movement.X, car.DriveSeat.Throttle)
	end
end)

-- Input begin
--game:GetService("UserInputService").InputBegan:connect(function(inputObject, gameProcessedEvent)
--	if not gameProcessedEvent then
--		if inputObject.KeyCode == Enum.KeyCode.W then
--			movement = Vector2.new(movement.X, 1)
--		elseif inputObject.KeyCode == Enum.KeyCode.A then
--			movement = Vector2.new(-1, movement.Y)
--		elseif inputObject.KeyCode == Enum.KeyCode.S then
--			movement = Vector2.new(movement.X, -1)
--		elseif inputObject.KeyCode == Enum.KeyCode.D then
--			movement = Vector2.new(1, movement.Y)
--		end
--	end
--end)
--
--game:GetService("UserInputService").InputChanged:connect(function(inputObject, gameProcessedEvent)
--	--if not gameProcessedEvent then
--		if inputObject.KeyCode == Enum.KeyCode.Thumbstick1 then
--			--Gamepad support because yay
--			if inputObject.Position.magnitude >= gamepadDeadzone then
--				movement = Vector2.new(movement.X, inputObject.Position.Y)
--			else
--				movement = Vector2.new(movement.X, 0)
--			end
--		elseif inputObject.KeyCode == Enum.KeyCode.Thumbstick2 then
--			if inputObject.Position.magnitude >= gamepadDeadzone then
--				movement = Vector2.new(inputObject.Position.X, movement.Y)
--			else
--				movement = Vector2.new(0, movement.Y)
--			end
--		end
--	--end
--end)
--
---- Input end
--game:GetService("UserInputService").InputEnded:connect(function(inputObject, gameProcessedEvent)
--	if inputObject.KeyCode == Enum.KeyCode.W then
--		if movement.Y == 1 then
--			movement = Vector2.new(movement.X, 0)
--		end
--	elseif inputObject.KeyCode == Enum.KeyCode.A then
--		if movement.X == -1 then
--			movement = Vector2.new(0, movement.Y)
--		end
--	elseif inputObject.KeyCode == Enum.KeyCode.S then
--		if movement.Y == -1 then
--			movement = Vector2.new(movement.X, 0)
--		end
--	elseif inputObject.KeyCode == Enum.KeyCode.D then
--		if movement.X == 1 then
--			movement = Vector2.new(0, movement.Y)
--		end
--	end
--end)

local force = 0
local damping = 0

local Mass = 0

for i, v in pairs(car:GetChildren()) do
	if v:IsA("BasePart") then
		Mass = Mass + (v:GetMass() * 196.2)
	end
end

force = Mass * stats.Suspension.Value
damping = force / stats.Bounce.Value

local bodyVelocity = Instance.new("BodyVelocity", car.Engine)
bodyVelocity.velocity = Vector3.new(0, 0, 0)
bodyVelocity.maxForce = Vector3.new(0, 0, 0)

local bodyAngularVelocity = Instance.new("BodyAngularVelocity", car.Engine)
bodyAngularVelocity.angularvelocity = Vector3.new(0, 0, 0)
bodyAngularVelocity.maxTorque = Vector3.new(0, 0, 0)

local rotation = 0

local function UpdateThrust(Thrust)
	--Make sure we have a bodythrust to move the wheel
	local bodyThrust = Thrust:FindFirstChild("BodyThrust")
	if not bodyThrust then
		bodyThrust = Instance.new("BodyThrust", Thrust)
	end
	--Do some raycasting to get the height of the wheel
	local hit, position = Raycast.new(Thrust.Position, Thrust.CFrame:vectorToWorldSpace(Vector3.new(0, -1, 0)) * stats.Height.Value)
	local ThrustHeight = (position - Thrust.Position).magnitude
	if hit and hit.CanCollide then
		--If we're on the ground, apply some forces to push the wheel up
		bodyThrust.force = Vector3.new(0, ((stats.Height.Value - ThrustHeight)^2) * (force / stats.Height.Value^2), 0)
		local ThrustDamping = Thrust.CFrame:toObjectSpace(CFrame.new(Thrust.Velocity + Thrust.Position)).p * damping
		bodyThrust.force = bodyThrust.force - Vector3.new(0, ThrustDamping.Y, 0)
	else
		bodyThrust.force = Vector3.new(0, 0, 0)
	end

	--Wheels
	local Weld = Thrust:FindFirstChild("Weld")
	if Weld then
		Weld.C0 = CFrame.new(0, -math.min(ThrustHeight, stats.Height.Value * 0.8) + (Weld.Part1.Size.Y / 2), 0)
		-- Wheel turning
		local offset = car.Engine.CFrame:inverse() * Thrust.CFrame
		local speed = car.Engine.CFrame:vectorToObjectSpace(car.Engine.Velocity)
		if offset.Z < 0 then
			local direction = 1
			if speed.Z > 0 then
				direction = -1
			end
			Weld.C0 = Weld.C0 * CFrame.Angles(0, (car.Engine.RotVelocity.Y / 2) * direction, 0)
		end
		Weld.C0 = Weld.C0 * CFrame.Angles(rotation, 0, 0)
	end
end

--A simple function to check if the car is grounded
local function IsGrounded()
	local hit, position = Raycast.new((car.Engine.CFrame * CFrame.new(0, 0, (car.Engine.Size.Z / 2) - 1)).p, car.Engine.CFrame:vectorToWorldSpace(Vector3.new(0, -1, 0)) * (stats.Height.Value + 0.2))
	if hit and hit.CanCollide then
		return(true)
	end
	return(false)
end

--local oldCameraType = camera.CameraType
--camera.CameraType = cameraType

--spawn(function()
while game:GetService("RunService").Heartbeat:wait() and car:FindFirstChild("DriveSeat") and character.Humanoid.SeatPart == car.DriveSeat do
	--game:GetService("RunService").RenderStepped:wait()
	if IsGrounded() then
		if movement.Y ~= 0 then
			local velocity = humanoidRootPart.CFrame.lookVector * movement.Y * stats.Speed.Value
			humanoidRootPart.Velocity = humanoidRootPart.Velocity:Lerp(velocity, 0.1)
			bodyVelocity.maxForce = Vector3.new(0, 0, 0)
		else
			bodyVelocity.maxForce = Vector3.new(Mass / 2, Mass / 4, Mass / 2)
		end
		local rotVelocity = humanoidRootPart.CFrame:vectorToWorldSpace(Vector3.new(movement.Y * stats.Speed.Value / 50, 0, -humanoidRootPart.RotVelocity.Y * 5 * movement.Y))
		local speed = -humanoidRootPart.CFrame:vectorToObjectSpace(humanoidRootPart.Velocity).unit.Z
		rotation = rotation + math.rad((-stats.Speed.Value / 5) * movement.Y)
		if math.abs(speed) > 0.1 then
			rotVelocity = rotVelocity + humanoidRootPart.CFrame:vectorToWorldSpace((Vector3.new(0, -movement.X * speed * stats.TurnSpeed.Value, 0)))
			bodyAngularVelocity.maxTorque = Vector3.new(0, 0, 0)
		else
			bodyAngularVelocity.maxTorque = Vector3.new(Mass / 4, Mass / 2, Mass / 4)
		end
		humanoidRootPart.RotVelocity = humanoidRootPart.RotVelocity:Lerp(rotVelocity, 0.1)

		--bodyVelocity.maxForce = Vector3.new(Mass / 3, Mass / 6, Mass / 3)
		--bodyAngularVelocity.maxTorque = Vector3.new(Mass / 6, Mass / 3, Mass / 6)
	else
		bodyVelocity.maxForce = Vector3.new(0, 0, 0)
		bodyAngularVelocity.maxTorque = Vector3.new(0, 0, 0)
	end

	for i, part in pairs(car:GetChildren()) do
		if part.Name == "Thrust" then
			UpdateThrust(part)
		end
	end
end
for i, v in pairs(car:GetChildren()) do
	if v:FindFirstChild("BodyThrust") then
		v.BodyThrust:Destroy()
	end
end
bodyVelocity:Destroy()
bodyAngularVelocity:Destroy()
--camera.CameraType = oldCameraType
script:Destroy()
--end)

RayCasyModule “ModuleScript”.

local module = {}

function module.new(startPosition, startDirection)
	local maxDistance = startDirection.magnitude
	local direction = startDirection.unit
	local lastPosition = startPosition
	local distance = 0
	local ignore = {}

	local hit, position, normal

	repeat
		local ray = Ray.new(lastPosition, direction * (maxDistance - distance))
		hit, position, normal = game.Workspace:FindPartOnRayWithIgnoreList(ray, ignore, false, true)
		if hit then
			if not hit.CanCollide then
				table.insert(ignore, hit)
			end
		end
		distance = (startPosition - position).magnitude
		lastPosition = position
	until distance >= maxDistance - 0.1 or (hit and hit.CanCollide)
	return hit, position, normal
end

return module

Please do not ask people to write entire scripts or design entire systems for you. If you can’t answer the three questions above, you should probably pick a different category.

So if anyone can help me do this it would be great so thanks.

THIS IS THE BASEPLATE!

Baseplate.rbxl (70.2 KB)

Tbh, I suggest doing the following:
Go inside the car model; make every part masless
then make a part make it as big and look at it’s mass grow, this will be the MassObject, make it CanCollide false.
Weld this to the Car Frame.
Note, move the MassObject a bit lower than the car so that there’s a lower chance to fall over; to make the Mass higher, just resize the MassObject.

In the first video it appears to be a physics issue with the wheels colliding with the body of the car when it leans in the corners, causing the bouncing.
In the Studio Settings you can turn on a property called ShowCollisions (I think, or something like it) that will put a red sphere wherever you have two items that touch each other. I find it useful when making physics based vehicles because sometimes you get items touching that you didn’t expect to which can cause issues.

That does not really do anything or help because i dont think it is banging on body

If you try it you may see the red spheres where the parts are contacting. It may give an idea of what’s going on.
The front tire, while rotated during steering, hits the front wheel well section and can push the body down, it bounces back against that force when it hits the end of the suspension travel and starts that kind of oscillation.
I may be totally wrong in your case, just that I’ve made suspension vehicles before and that’s what it looked like to me. Doesn’t hurt at all to try it out.

The wheels dont collide because they are not collidding is of CanCollide is set to false