Raycast Car works at 60Fps but not at 30 or lower

So i have a raycast car script that works fine on 60Fps but when the client runs at 30fps or lower the car starts to wobble.

im not sure how to fix it i have looked at other devforum post about this topic but it didnt help.

60Fps:

30Fps:

The code is from a tutorial i followed i just changed it from ApplyImpulse to VectorForces.

Here is the code:

			for wheelName,pos in pairs(props.WheelPositions)do
				local Att = Instance.new("Attachment")
				Att.Parent = carPrim
				Att.WorldPosition = carPrim.CFrame:ToWorldSpace(CFrame.new(pos)).p
				print(Att)
				local vectorForce = Instance.new("VectorForce", Att)
				vectorForce.Force = Vector3.new(0,0,0)
				vectorForce.Attachment0 = Att
				vectorForce.RelativeTo = Enum.ActuatorRelativeTo.World
				
				VectorForces[wheelName] = {}
				VectorForces[wheelName].attachment = Att
				VectorForces[wheelName].vector = vectorForce
				SpringLengthMemory[wheelName] = 0.5 
			end
		
			while true do
				local delta = game["Run Service"].Heartbeat:Wait()
				local carPrim = carModel.PrimaryPart
				
				SmoothSteer = math.abs(
					seatPart.SteerFloat-SmoothSteer)<=delta*5 and seatPart.SteerFloat or SmoothSteer+math.sign(
					seatPart.SteerFloat-SmoothSteer)*delta*5
				
				for wheelName, originalPosition in pairs(props.WheelPositions)do
					local Att = VectorForces[wheelName].attachment
					local wheelVF = VectorForces[wheelName].vector
					
					local carCFrame = carPrim.CFrame
					local rayOrigin = carCFrame:ToWorldSpace(CFrame.new(originalPosition)).p
					local rayDirection = -carCFrame.UpVector * (props.SuspensionMaxLength + props.wheelRadius)
					local rayParams = RaycastParams.new()
					rayParams.FilterDescendantsInstances = {carModel}
					local raycast = workspace:Raycast(rayOrigin,rayDirection,rayParams)
					if raycast then
						
						Att.WorldPosition = raycast.Position
						local RaycastDistance = (rayOrigin - raycast.Position).magnitude

						local SpringLength = math.clamp(RaycastDistance - props.wheelRadius, 0, props.SuspensionMaxLength)
						local StiffnessForce = props.Stiffness * (props.SuspensionMaxLength - SpringLength)
						local DamperForce = props.Damper * (( SpringLengthMemory[wheelName] - SpringLength) / (delta))
						local SuspensionForceVec3 = carCFrame.UpVector * (StiffnessForce + DamperForce)
						local YSuspensionForce = Vector3.new(0,SuspensionForceVec3.Y,0)
						
						local RotationsOnlyWheelDirCFrame = CFrame.lookAt(Vector3.zero,carCFrame.LookVector,carCFrame.UpVector)
						if string.sub(wheelName,1,1)=='F' then
							RotationsOnlyWheelDirCFrame = RotationsOnlyWheelDirCFrame * CFrame.Angles(0,-math.rad(SmoothSteer * props.SteerAngle),0)
						end
						local LocalVelocity = RotationsOnlyWheelDirCFrame:ToObjectSpace(CFrame.new(carPrim:GetVelocityAtPosition(raycast.Position)))

						local Xforce = RotationsOnlyWheelDirCFrame.RightVector * -LocalVelocity.x * props.wheelFriction
						
						local fuel = carModel:GetAttribute("fuel")
						local throttleForce = 0
						if fuel and fuel > 0 and not carModel.Destroyed.Value then
							-- Only apply throttle force if fuel is available
							throttleForce = seatPart.ThrottleFloat * carModel:GetAttribute("torque") * (
								(math.sign(-LocalVelocity.z) == seatPart.Throttle and (1 - math.min(1, math.abs(LocalVelocity.z) / carModel:GetAttribute("maxSpeed")))) or 1
							)
						end

						local Zforce = RotationsOnlyWheelDirCFrame.LookVector * throttleForce
						SpringLengthMemory[wheelName] = SpringLength
						
						wheelVF.Force = (SuspensionForceVec3 + Xforce  + Zforce) * 60
						--carPrim:ApplyImpulseAtPosition((SuspensionForceVec3 + Xforce + Zforce) * delta * 60, raycast.Position)

					else
						wheelVF.Force = Vector3.new(0,0,0)
						SpringLengthMemory[wheelName] = props.SuspensionMaxLength
					end


					if VisualiseRaycast then PlrGui.gaag.ag.CurrentCamera = workspace.CurrentCamera PlrGui.gaag.ag2.CurrentCamera = workspace.CurrentCamera coroutine.wrap(function()local p = Instance.new("Part",PlrGui.gaag.ag)p.Name = 'rayVis'p.Color = Color3.new(0,0,1)p.Anchored = true p.CanCollide = false p.CollisionGroupId = 1 p.Material = Enum.Material.Neon p.Shape = Enum.PartType.Cylinder local pe = p:Clone()pe.Color = Color3.new(1,0,0)pe.Parent = p.Parent local Rdestination = (rayOrigin + rayDirection)if raycast then local rDist = (rayOrigin - raycast.Position).magnitude local s = rayOrigin - carCFrame.UpVector * rDist/2 p.CFrame = CFrame.lookAt(s, s + carCFrame.RightVector,-carCFrame.LookVector) p.Size = Vector3.new(rDist, 0.1, 0.1)rDist = (raycast.Position - Rdestination).magnitude s =  raycast.Position - carCFrame.UpVector * rDist/2 pe.Size = Vector3.new(rDist, 0.1, 0.1)pe.CFrame = CFrame.lookAt(s, s + carCFrame.RightVector,-carCFrame.LookVector)else p.Size = Vector3.new((rayOrigin - Rdestination).magnitude, 0.1, 0.1)local s = rayOrigin + rayDirection/2 p.CFrame = CFrame.lookAt(s, s + carCFrame.RightVector,-carCFrame.LookVector)Rdestination = Rdestination + carCFrame.UpVector*0.02 pe.CFrame = CFrame.lookAt(Rdestination, Rdestination + carCFrame.RightVector,-carCFrame.LookVector)pe.Size = Vector3.new(0,0.1,0.1)end local pp = p:Clone()pp.Parent = PlrGui.gaag.ag2 pp.Size = p.Size + Vector3.new(0.01,0.01,0.01)local ppe = pe:Clone() ppe.Parent = pp.Parent ppe.Size = pe.Size + Vector3.new(0.01,0.01,0.01)game["Run Service"].Heartbeat:Wait()pp:Destroy()ppe:Destroy()p:Destroy()pe:Destroy()end)()end 

				end``` The *60 at the end of Force is for the Mass i think because when i didnt have it * 60 it didnt have enough force to push it self of the ground. The model itself is massless and the chassis consist of one part which has mass of 62.

You’re manually scaling forces by * 60 assuming 60 FPS, but delta isn’t being used properly.

wheelVF.Force = (SuspensionForceVec3 + Xforce + Zforce) / delta
1 Like

This actually is the common advice but does not work since the physics calculation is not based on dt.

Its fundamentally because forces are applied at 60hz which is unstable.

My solution was to use physics substepping and basically recreate the physics engine…it works a bit but adding a driving force, friction will be more complicated.

I think honestly just wait untill roblox adds it in. I saw another game like retail tycoon 2 make their vehicles switch to physics constraints when the fps is low.

2 Likes

So do the most car games use physic cars like contrainsts because they are “better”? or do they have that substepping implemented that u mentioned? If everyone uses constraints i wonder why jailbreak doesnt use physic contraint cars as well?

Most games ( from my experience idk truly) use physics constraints as yeah they are integrated with the roblox engine and dont suffer from low fps (havent fully tested but should be the case)

Jailbreak still uses vector force suspension because it is arcadey and unique.

There are a lot of memes and reddit post that make fun of this.

https://www.google.com/url?sa=t&source=web&rct=j&opi=89978449&url=https://www.reddit.com/r/robloxjailbreak/comments/wwb0h1/why_does_my_car_keep_shaking_all_the_time/&ved=2ahUKEwiw8NPd-PqMAxWRXUEAHfCXPLUQrAJ6BAg-EAU&usg=AOvVaw0jshd5WZEhESMTmdiZ06Bt

The best you could do is to lower the stiffness of the spring at low fps. It will make the vehicle feel different in performance however.