Hello! Ive been making a spherecast / raycast car suspension system and Ive been running into a problem. The car suspension works perfectly except the fact that the car moves like its on ice. I figure it needs friction but Im not sure how to implement this to my current math so I was wondering if anyone could help me with this.
This is my suspension module code:
-- Services
local replicatedStorage = game:GetService("ReplicatedStorage")
local runService = game:GetService("RunService")
-- Modules
local debugEffects = require(replicatedStorage.Effects.Debug)
-- Module
local suspension = {}
suspension.__index = suspension
local debugEffect
-- Functions
local function absoluteVector(vector : Vector3)
return Vector3.new(math.abs(vector.X), math.abs(vector.Y), math.abs(vector.Z))
end
function suspension.new(vehicle : Model, wheelAttachment : Attachment)
local self = setmetatable({}, suspension)
self.vehicle = vehicle
self.wheel = wheelAttachment
self.wheelPart = self.vehicle.Wheels[self.wheel.Name]
self.vectorForce = self.wheel.VectorForce
self.restLength = self.wheelPart:GetAttribute("RestLength")
self.wheelRadius = self.wheelPart:GetAttribute("WheelRadius")
self.springStiffness = self.wheelPart:GetAttribute("SpringStiffness")
self.springTravel = self.wheelPart:GetAttribute("SpringTravel")
self.damperStiffness = self.wheelPart:GetAttribute("DamperStiffness")
self.minLength = (self.restLength - self.springTravel)
self.maxLength = (self.restLength + self.springTravel)
self.lastLength = self.wheelRadius
self.spherecast = nil
self.debug = true
if self.debug then
self.debugOrigin = self.vehicle.Debug.Origin[string.gsub(self.wheel.Name, "Wheel", "Origin")]
self.debugEnd = self.vehicle.Debug.End[string.gsub(self.wheel.Name, "Wheel", "End")]
self.debugSphere = self.vehicle.Debug.Spheres[string.gsub(self.wheel.Name, "Wheel", "Sphere")]
self.debugRay = self.debugOrigin.Beam
end
self.raycastParams = RaycastParams.new()
self.raycastParams.FilterType = Enum.RaycastFilterType.Exclude
self.raycastParams.FilterDescendantsInstances = {self.vehicle}
return self
end
function suspension:update(deltaTime : number)
self:updateProperties()
self:calculatePhysics(deltaTime)
self:applyForce()
end
function suspension:updateProperties()
self.restLength = self.wheelPart:GetAttribute("RestLength")
self.wheelRadius = self.wheelPart:GetAttribute("WheelRadius")
self.springStiffness = self.wheelPart:GetAttribute("SpringStiffness")
self.springTravel = self.wheelPart:GetAttribute("SpringTravel")
self.damperStiffness = self.wheelPart:GetAttribute("DamperStiffness")
end
function suspension:calculatePhysics(deltaTime : number)
self.spherecast = workspace:Spherecast(self.wheel.WorldPosition, self.wheelRadius / 2, -self.vehicle.PrimaryPart.CFrame.UpVector * self.wheelRadius, self.raycastParams)
if self.spherecast then
self:updateDebug(self.wheel.WorldPosition)
local extension = self.wheelRadius - self.spherecast.Distance
local relativeVelocity = self.vehicle.PrimaryPart.CFrame:VectorToObjectSpace(self.wheelPart.Velocity)
self.springLength = (self.spherecast.Distance - self.wheelRadius)
local springLength = math.clamp(self.springLength, self.minLength, self.maxLength)
self.springVelocity = (self.lastLength - self.springLength) / deltaTime
self.springForce = self.vehicle.PrimaryPart.CFrame.UpVector * (self.springStiffness * springLength - self.damperStiffness * relativeVelocity.Y)
self.damperForce = self.damperStiffness * self.springVelocity
self.suspensionForce = (Vector3.new(0, self.springForce, 0) + Vector3.new(0, self.damperForce, 0))
self.lastLength = self.springLength
end
end
function suspension:updateDebug(origin : Vector3)
if not self.debug then return end
self.raycast = workspace:Raycast(self.wheel.WorldPosition, -self.vehicle.PrimaryPart.CFrame.UpVector * self.wheelRadius * 100, self.raycastParams)
if self.spherecast and self.raycast then
local distance = (origin - self.raycast.Position)
local position = Vector3.new(origin.X, distance.Y, origin.Z)
self.debugRay.Color = ColorSequence.new(Color3.fromRGB(255, 220, 23))
self.debugOrigin.Position = origin
self.debugEnd.Position = self.raycast.Position
local radius = self.wheelRadius / 2
self.debugSphere.Position = self.raycast.Position + Vector3.new(0, radius, 0)
self.debugSphere.Size = Vector3.new(radius * 2, radius * 2, radius * 2)
self.debugOrigin.Orientation = Vector3.zero
self.debugEnd.Orientation = Vector3.zero
elseif not self.raycast or self.spherecast then
self.debugRay.Color = ColorSequence.new(Color3.fromRGB(73, 255, 7))
end
end
function suspension:applyForce()
if not self.spherecast then self.vectorForce.Force = Vector3.zero return end
self.vectorForce.Force = self.suspensionForce
end
-- Returning
return suspension
This code is ran on the server in a loop for now.
Heres an example of the car skating: