Skip to content

Commit 760766c

Browse files
committed
fix: stick laterally on incline
1 parent 86675d6 commit 760766c

5 files changed

Lines changed: 39 additions & 23 deletions

File tree

src/landvehicle/src/Client/Classes/Suspensions/WheelVehicleSuspensionClient.luau

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ function Suspension.new(vehicleObj: Model, config: WheelVehicleTypes.SuspensionC
101101
-- self.force.RelativeTo = Enum.ActuatorRelativeTo.Attachment0
102102
self.force.Force = Vector3.new(0, 0, 0)
103103
self.force.Attachment0 = self.forceAttachment
104+
self.force.Visible = true
104105
self.force.Parent = self.forceAttachment
105106
self._maid.relativeForce = self.force
106107

src/landvehicle/src/Client/Classes/Suspensions/WheelVehicleSuspensionControllerClient.luau

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,19 +120,17 @@ end
120120

121121
function SuspensionController.UpdateStepped(self: SuspensionController, deltaTimeSim: number)
122122
-- Makeshift speedometer
123-
-- print(self._vehicleObj.PrimaryPart.AssemblyLinearVelocity.Magnitude * (1 / 3.57) * 3.6, "km/h")
123+
-- print((self._vehicleObj.PrimaryPart :: BasePart).AssemblyLinearVelocity.Magnitude * (1 / 3.57) * 3.6, "km/h")
124124

125125
for _, suspension in pairs(self._suspensions) do
126126
suspension:UpdateStepped(deltaTimeSim)
127127

128128
-- NOTE: A very basic implementation to get the car moving.
129-
if suspension.wheel then
130-
local brakeInputScaled = self.brakeInput
131-
if suspension.wheel.angularVelocity > 0 then
132-
brakeInputScaled *= 6
133-
end
134-
suspension.wheel.baseTorque = (self.throttleInput - brakeInputScaled) * 20
129+
local brakeInputScaled = self.brakeInput
130+
if suspension.wheel.angularVelocity > 0 then
131+
brakeInputScaled *= 6
135132
end
133+
suspension.wheel.baseTorque = (self.throttleInput - brakeInputScaled) * 20
136134
end
137135

138136
for _, steeringSuspension in pairs(self._steeringSuspensions) do

src/landvehicle/src/Client/Classes/WheelVehicleClient.luau

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ function WheelVehicleClient:_createSuspensions()
275275
wheelObj,
276276
suspensionInstance._config.wheel,
277277
WheelVehicleUtils:GetLinks(self._obj, suspension.position),
278+
suspensionInstance.forceAttachment,
278279
self._config.chassis.mass
279280
)
280281
suspensionInstance.wheel = wheelInstance

src/landvehicle/src/Client/Classes/Wheels/WheelVehiclePacejkaWheelClient.luau

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
44
See http://www-cdr.stanford.edu/dynamic/bywire/tires.pdf.
55
Used as reference for this implementation.
6+
Note that this is a Pacejka '94 version model. Not '89.
67
78
TODO: Massive simplifications and optimisations are possible once everything is finalised.
89
@@ -25,13 +26,15 @@ local REALISTIC_GRAVITY = 35 -- 35 studs/s^2 -> 9.8 m/s^2
2526
local PI = math.pi
2627
local TWO_PI = PI * 2
2728
local sin = math.sin
29+
local tan = math.tan
2830
local atan = math.atan
2931
local atan2 = math.atan2
3032
local abs = math.abs
3133
local min = math.min
3234
local exp = math.exp
3335
local deg = math.deg
3436
local sign = math.sign
37+
local max = math.max
3538
local euler = exp(1)
3639

3740
local PacejkaWheel = setmetatable({}, WheelVehicleWheelClient)
@@ -40,7 +43,7 @@ PacejkaWheel.__index = PacejkaWheel
4043

4144
export type PacejkaWheel =
4245
typeof(setmetatable(
43-
{} :: { _vehicleMass: number, _torqueToCarForceRatio: number },
46+
{} :: { _vehicleMass: number, _torqueToCarForceRatio: number, _wheelInertia: number },
4447
{} :: typeof({ __index = PacejkaWheel })
4548
))
4649
& WheelVehicleWheelClient.Wheel
@@ -49,9 +52,10 @@ function PacejkaWheel.new(
4952
wheelObj: BasePart,
5053
wheelConfig: WheelVehicleTypes.WheelConfig,
5154
links: WheelVehicleUtils.Links,
55+
_forceAttachment: Attachment,
5256
_vehicleMass: number
5357
): PacejkaWheel
54-
local self = setmetatable(WheelVehicleWheelClient.new(wheelObj, wheelConfig, links), PacejkaWheel)
58+
local self = setmetatable(WheelVehicleWheelClient.new(wheelObj, wheelConfig, links, _forceAttachment), PacejkaWheel)
5559

5660
self._vehicleMass = _vehicleMass
5761

@@ -90,9 +94,9 @@ end
9094
function PacejkaWheel.SetConstants(self: PacejkaWheel)
9195
WheelVehicleWheelClient.SetConstants(self)
9296

93-
local wheelInertia = 1 / 2 * self.mass * self.radius ^ 2
94-
self._torqueToCarForceRatio = (wheelInertia / self.radius ^ 2)
95-
/ (self._vehicleMass + wheelInertia / self.radius ^ 2)
97+
self._wheelInertia = 1 / 2 * self.mass * self.radius ^ 2
98+
self._torqueToCarForceRatio = (self._wheelInertia / self.radius ^ 2)
99+
/ (self._vehicleMass + self._wheelInertia / self.radius ^ 2)
96100
end
97101

98102
function PacejkaWheel.UpdateStepped(
@@ -130,7 +134,8 @@ function PacejkaWheel.UpdateStepped(
130134
local torque = self.baseTorque -- +ve when accelerating, -ve when braking
131135
local lateralForce = 0
132136
local longitudinalForce = 0
133-
local longitudinalSlip = longitudeVelocity - self.angularVelocity * self.radius -- -ve when accelerating, +ve when braking.
137+
local wheelContactVelocity = self.angularVelocity * self.radius
138+
local longitudinalSlip = longitudeVelocity - wheelContactVelocity -- -ve when accelerating, +ve when braking.
134139

135140
-- Pacejka mysterious and magical constants.
136141
-- See http://www-cdr.stanford.edu/dynamic/bywire/tires.pdf.
@@ -167,11 +172,11 @@ function PacejkaWheel.UpdateStepped(
167172

168173
if abs(speed) > 2.5 then
169174
-- Lateral force component (F_y -> Fy)
170-
local FyC = 1.30
171-
local FyD = -22.1 * Fz ^ 2 + 1011 * Fz
172-
local FyBCD = 1078 * sin(1.82 * atan(0.208 * Fz))
175+
local FyC = 1.35 -- 1.30
176+
local FyD = 1.00 * Fz * 1000 -- -22.1 * Fz ^ 2 + 1011 * Fz
177+
local FyBCD = (13.5 * Fz ^ 1.07) * 1000 -- 1078 * sin(1.82 * atan(0.208 * Fz))
173178
local FyB = FyBCD / (FyC * FyD)
174-
local FyE = 0 * Fz ^ 2 - 0.354 * Fz + 0.707
179+
local FyE = 0.78 - 0.025 * Fz -- 0 * Fz ^ 2 - 0.354 * Fz + 0.707
175180

176181
-- Lateral force camber angle factor
177182
local FySh = 0.028 * camberAngle
@@ -187,13 +192,21 @@ function PacejkaWheel.UpdateStepped(
187192
-- * REALISTIC_GRAVITY
188193
-- * (sin(contactLinkOrientationX + PI / 2) * (1 - lateralVelocity * 0.4) - lateralVelocity * 0.1)
189194

190-
-- NOTE: Lazy solution is using the suspension's global force
191-
lateralForce = lateralVelocity * 900
195+
local forceAttachmentRightVector = self._forceAttachment.WorldCFrame.RightVector
196+
local y = math.atan2(forceAttachmentRightVector.Z, forceAttachmentRightVector.X)
197+
local relativeRightVector = CFrame.Angles(0, -y, 0)
198+
:VectorToObjectSpace(self._forceAttachment.WorldCFrame.RightVector)
199+
200+
lateralForce = verticalForce * tan(atan2(relativeRightVector.X, relativeRightVector.Y) - PI / 2)
201+
+ lateralVelocity * 900
192202
end
193203

194204
-- Longitudinal force component (F_x -> Fx)
195205
if longitudeVelocity ~= 0 then
196-
percentLongitudinalSlip = longitudinalSlip / abs(longitudeVelocity) * 100 * min(1, abs(speed) * 0.07)
206+
percentLongitudinalSlip = longitudinalSlip
207+
/ max(abs(longitudeVelocity), abs(wheelContactVelocity))
208+
* 100
209+
* min(1, abs(speed) * 0.07)
197210

198211
local FxC = 1.65
199212
local FxD = -21.3 * Fz ^ 2 + 1144 * Fz
@@ -255,6 +268,7 @@ function PacejkaWheel.UpdateStepped(
255268
-- deg(atan2(contactNormal.Y, contactNormal.Z)),
256269
-- deg(atan2(contactNormal.Y, contactNormal.X))
257270
-- )
271+
258272
-- print("-")
259273
end
260274
end
@@ -276,7 +290,7 @@ function PacejkaWheel.UpdateStepped(
276290
-- Wheel rotation
277291
-- local angularVelocityRelative = longitudeVelocity / self.radius - self.angularVelocity
278292

279-
self.angularVelocity += torque * deltaTimeSim
293+
self.angularVelocity += torque / self._wheelInertia * deltaTimeSim
280294

281295
self.rotation += self.angularVelocity * deltaTimeSim
282296
if (self.rotation :: number) > TWO_PI then

src/landvehicle/src/Client/Classes/Wheels/WheelVehicleWheelClient.luau

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ export type Wheel = typeof(setmetatable(
2828
_sign: number,
2929
_lastUpdated: number,
3030
_previousLongitudeVelocity: number,
31+
_forceAttachment: Attachment,
3132
attachment: Attachment,
3233
angularVelocity: number, -- radians per second
3334
baseTorque: number,
3435
radius: number,
35-
force: VectorForce,
3636
rotation: number, -- radians
3737
mass: number,
3838
},
@@ -42,7 +42,8 @@ export type Wheel = typeof(setmetatable(
4242
function Wheel.new(
4343
wheelObj: BasePart,
4444
wheelConfig: WheelVehicleTypes.WheelConfig,
45-
links: WheelVehicleUtils.Links
45+
links: WheelVehicleUtils.Links,
46+
forceAttachment: Attachment
4647
): Wheel
4748
local attachment = wheelObj:FindFirstChild("RigidAttachment")
4849
assert(attachment, "Expected wheelObj to have a RigidAttachment")
@@ -51,6 +52,7 @@ function Wheel.new(
5152

5253
self._config = wheelConfig
5354
self._links = links
55+
self._forceAttachment = forceAttachment
5456
self._previousLongitudeVelocity = 0
5557
self.angularVelocity = 0
5658
self.baseTorque = 0

0 commit comments

Comments
 (0)