I have recently replaced my cars physics based suspension with a code based one, which while being far better overall has a flaw I need help addressing.
The suspension system I use is prone to Jittering for unknown reasons:
Here is the video I sourced the code from:
Even In the original system the cars suspension is prone to Jittering, I am 99% sure this problem is not a frame rate issue as I modified the code heavily to work with all frame rates and tested it thoroughly,
Local Car:
Humanoid.Seated:Connect(function(seated,seatPart)
if seated and seatPart then
local vehicle = seatPart.Parent
local carPrim = vehicle.PrimaryPart
local id = vehicle:GetAttribute("CarID")
local props = carProps[id]
local carModel = seatPart.Parent
local carPrim = carModel.PrimaryPart
local SpringLengthMemory = {}
for i,v in pairs(props.WheelPositions)do SpringLengthMemory[i] = 0.5 end
--current steer pos
local SmoothSteer = 0
local serverTakeOverWait = 0
while true do
if seatPart.Parent:FindFirstChild("VehicleIdentifierID") and seatPart:IsA("VehicleSeat")then--whatever that gives out its car
local delta = game["Run Service"].Heartbeat:Wait()
for i,v in pairs(props.WheelPositions)do
local carCFrame = carPrim.CFrame
local rayOrigin = carCFrame:ToWorldSpace(CFrame.new(Vector3.new(v.x,v.y - props.wheelRadius,v.z))).p
local rayDirection = -carCFrame.UpVector * props.SuspensionMaxLength
local rayParams = RaycastParams.new()
rayParams.FilterDescendantsInstances = {vehicle}
local raycast = workspace:Raycast(rayOrigin,rayDirection,rayParams)
if raycast then
local LocalVelocity = CFrame.lookAt(Vector3.zero,carCFrame.LookVector,carCFrame.UpVector):ToObjectSpace(CFrame.new(carPrim:GetVelocityAtPosition(raycast.Position)))
if not WheelRotations[vehicle] then
WheelRotations[vehicle] = {}
WheelRotations[vehicle].STEER = 0
for i,v in pairs(props.WheelPositions)do WheelRotations[vehicle][i] = 0.5 end
end
WheelRotations[vehicle][i] += LocalVelocity.z * delta
local DatWeld = vehicle.Wheels[i].Weld
local weldInitPos = CFrame.new(v - Vector3.new(0,(rayOrigin - raycast.Position).magnitude,0))
if string.sub(i,1,1)=='F' then--if front
--lerp smoothing below, but for some reason SteerFloat flickers
WheelRotations[vehicle].STEER = WheelRotations[vehicle].STEER + (vehicle[props.driverSeatName].SteerFloat - WheelRotations[vehicle].STEER)*delta*6
DatWeld.C0 = weldInitPos * CFrame.Angles(0,-math.rad(WheelRotations[vehicle].STEER * props.SteerAngle),0)
else--DatWeld.C0.Rotation
DatWeld.C0 = weldInitPos
end
if v.x>0 then
DatWeld.C0 = DatWeld.C0 * CFrame.Angles(0,math.rad(180),0)
DatWeld.C0 = DatWeld.C0 * CFrame.Angles(-WheelRotations[vehicle][i],0,0)
else
DatWeld.C0 = DatWeld.C0 * CFrame.Angles(WheelRotations[vehicle][i],0,0)
end
end
end
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 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
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) * 100 * delta
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 * delta * 25
local Zforce = RotationsOnlyWheelDirCFrame.LookVector * seatPart.ThrottleFloat * props.torque * (math.sign(-LocalVelocity.z)==seatPart.Throttle and(1 - math.min(1,math.abs(LocalVelocity.z)/props.MaxSpeed))or 1) * 100 * delta
SpringLengthMemory[wheelName] = SpringLength
carPrim:ApplyImpulseAtPosition(SuspensionForceVec3 + Xforce + Zforce, raycast.Position)
else
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
if seatPart.Occupant ~= Humanoid then--check whatever that means that character still sits in car
serverTakeOverWait += delta
if serverTakeOverWait >= 0.5 then
break
end
end
end
end
end
end)
Car Properties:
local module = {
Wingman = {
driverSeatName = "FLSeat",
SuspensionMaxLength = 2.8,
wheelRadius = 1.4875,
Stiffness = 45,
Damper = 4,
wheelFriction = 6.5,
torque = 35,
MaxSpeed = 90,
SteerAngle = 30,
WheelPositions = {
RL = Vector3.new(3.424, 0, 5.8),
RR = Vector3.new(-3.438, 0, 5.8),
FL = Vector3.new(3.423, 0, -6.12),
FR = Vector3.new(-3.438, 0, -6.12),
},
},
}
return module
I would greatly appreciate if anyone could think of the reason why the car suspension does this, Thank you for your time.