Raycast suspensions go nuts when player goes on the car

Hey devs, I’ve been trying to make some raycast suspensions for my car controller recently and it all worked fine until I noticed something weird: when I added a VehicleSeat and made the player hop on it, the car just fell to the ground. Then, a few seconds after, it would plummet in the sky…

I did some testing and noticed the following behavior: on the CLIENT, the car seems to fall on the floor; on the SERVER, the car is “floating” mid-air, at exactly the maximum height for the suspensions, until at some random point it updates to the same position as the client.
It seems like since the server thinks the car is floating it applies no suspension force, meaning the one on the client stays on the ground; then, when it randomly updates to the correct position, since the distance between the suspensions and the ground is basically 0 the force becomes absurdly high and launches me in the air.

Does anybody have any idea why this desync is happening? Here’s a video and the code for my suspensions:

local RunService = game:GetService("RunService")

local CarModel = script.Parent
local Root = CarModel.CarRoot
local Wheels = Root.Wheels:GetChildren()

local Config = { -- I use a dynamic config table but this is basically what the code in my video example works with
	Suspensions = {
		Damper = 10000 -- 10k
		Stiffness = 140000 -- 140k; this high since part denisty is set to 100
		MaxExtension = 2 -- How far the suspensions can extend
		RestLen = 2 -- The spring length at rest
		WheelRadius = 1
		MinLen = 0 -- RestLen - MaxExtension
		MaxLen = 4 -- RestLen + Max Extension
	}
}

for _, part in CarModel:GetDescendants() do
	if part:IsA("BasePart") then
		part:SetNetworkOwner(nil)
	end
end

--// Suspensions
local lastSpringLen = {}

for _, Wheel:Attachment in Wheels do
	lastSpringLen[Wheel.Name] = 0
	local wheelForce = Instance.new("VectorForce", Root.Suspensions)
	wheelForce.Visible = true
	wheelForce.Attachment0 = Wheel
	wheelForce.Force = Vector3.new(0,0,0)
	wheelForce.Name = Wheel.Name
end

RunService.Stepped:Connect(function(_,dt)
	
	local rcParams = RaycastParams.new()
	rcParams.FilterDescendantsInstances = {CarModel}
	if CarModel.Seats.Driver.Occupant then rcParams:AddToFilter(CarModel.Seats.Driver.Occupant.Parent) end
	rcParams.FilterType = Enum.RaycastFilterType.Exclude
	
	local lconfig = Config.Suspensions
	
	for _, Wheel:Attachment in Wheels do
		
		local SpringLen = lconfig.RestLen
		
		local suspRay = workspace:Raycast(Wheel.WorldPosition,-Wheel.WorldSecondaryAxis*lconfig.MaxLen,rcParams)
		if suspRay then
			print("Ray length: "..suspRay.Distance)
			Wheel.RayVis.Length = suspRay.Distance
			SpringLen = math.clamp(suspRay.Distance - lconfig.WheelRadius,lconfig.MinLen,lconfig.MaxLen)
		else
			Wheel.RayVis.Length = lconfig.MaxLen
			print("Ray hit nothing")
		end
		
		local SpringForce = lconfig.Stiffness * (lconfig.RestLen - SpringLen)
		local DampForce = lconfig.Damper * (lastSpringLen[Wheel.Name] - SpringLen) / dt
		
		Root.Suspensions[Wheel.Name].Force = Vector3.new(0,SpringForce + DampForce,0)
		
		lastSpringLen[Wheel.Name] = SpringLen
		
	end
	
end)

Anything helps!

1 Like

this is highly likely to be caused by the network owner, i know you set it to nil but when a player is seated, even if the part is set to have networkowner to nil, the actual networkowner is neither the server nor the client (from what i remember) so i suggest you should try to swap to the client (for the suspension and networkowner) when well a player goes on the car

1 Like

Oh I see, I didn’t know ownerhsips changed when the player is seated. Makes sense why the desync is happening then, yeah.
I’ll try to find a workaround to this, then. Thank you!

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.