slomal bahui

This commit is contained in:
Никита Круглицкий 2025-05-14 18:57:41 +06:00
parent 6a128f470b
commit 138d91b9f9
12 changed files with 233 additions and 86 deletions

View File

@ -31,16 +31,21 @@ local WheelConfig = {
D = 0.10, D = 0.10,
E = -2.5 E = -2.5
}, },
-- AligningFrictionPreset = {
-- B = 13,
-- C = 2.4,
-- D = 1,
-- E = 0.48
-- }
}, },
Model = 'models/sprops/trans/wheel_a/wheel25.mdl' Model = 'models/sprops/trans/wheel_a/wheel25.mdl'
} }
local FrontWheelsConfig = table.merge( local FrontWheelsConfig = table.merge(
table.copy(WheelConfig), table.copy(WheelConfig),
{ {
SteerLock = 33,
CustomWheel = { CustomWheel = {
CasterAngle = 7, CasterAngle = 7,
CamberAngle = -6, CamberAngle = -3,
ToeAngle = 0.5 ToeAngle = 0.5
} }
} }
@ -51,7 +56,7 @@ local RearWheelsConfig = table.merge(
{ {
HandbrakePower = 2200, HandbrakePower = 2200,
CustomWheel = { CustomWheel = {
CamberAngle = -1, CamberAngle = -2,
ToeAngle = 0.5 ToeAngle = 0.5
} }
} }
@ -94,6 +99,7 @@ Vehicle:new({
Type = POWERTRAIN_COMPONENT.Gearbox, Type = POWERTRAIN_COMPONENT.Gearbox,
Config = { Config = {
Type = 'MANUAL', Type = 'MANUAL',
ShiftTime = 3,
Inertia = 0.01, Inertia = 0.01,
Ratios = { 3.626, 2.200, 1.541, 1.213, 1.000, 0.767 }, Ratios = { 3.626, 2.200, 1.541, 1.213, 1.000, 0.767 },
Reverse = 3.437 Reverse = 3.437
@ -139,7 +145,7 @@ Vehicle:new({
CoastRamp = 1, CoastRamp = 1,
PowerRamp = 1, PowerRamp = 1,
Stiffness = 0.1, Stiffness = 0.1,
SlipTorque = 0 SlipTorque = 0,
} }
}, },
{ {
@ -153,7 +159,7 @@ Vehicle:new({
BiasAB = 0.5, BiasAB = 0.5,
CoastRamp = 1, CoastRamp = 1,
PowerRamp = 1, PowerRamp = 1,
Stiffness = 0.1, Stiffness = 1,
SlipTorque = 0 SlipTorque = 0
} }
}, },

View File

@ -0,0 +1,85 @@
--@client
local fontArial92 = render.createFont("Arial", 92, 250, true, false, false, false, 0, false, 0)
local fontArial46 = render.createFont("Arial", 46, 250, true, false, false, false, 0, false, 0)
ENGINE_RPM, CAR_SPEED, GEARBOX_GEAR = 0, 123, 0
local resx, resy = render.getGameResolution()
local linesmatrix = Matrix()
local linesposx, linesposy = resx - 200, resy - 150
linesmatrix:setTranslation(Vector(linesposx, linesposy, 0))
linesmatrix:setAngles(Angle(0, 15, 0))
linesmatrix:setScale(Vector(0.7))
hook.add("DrawHud", "CARHUD", function()
render.pushMatrix(linesmatrix)
render.enableScissorRect(linesposx - 50, linesposy + 30, linesposx + 208, linesposy + 50)
for x = 0, 208, 4 do
if ENGINE_RPM > x / 220 then
col = 55 + x / 220 * 200
render.setRGBA(col, col, col, 250)
else
render.setRGBA(100, 100, 100, 250)
end
render.drawRectFast(x, -x / 2, 2, 150)
end
render.disableScissorRect()
render.enableScissorRect(linesposx - 50, linesposy + 20, linesposx + 212, linesposy + 50)
for x = 212, 220, 4 do
if ENGINE_RPM > x / 220 then
render.setRGBA(200, 71, 71, 200)
else
render.setRGBA(100, 100, 100, 200)
end
render.drawRectFast(x, -x / 2, 2, 150)
end
render.disableScissorRect()
render.enableScissorRect(linesposx - 50, linesposy + 20, linesposx + 208, linesposy + 25)
render.drawRectFast(0, -256, 208, 512)
render.disableScissorRect()
render.popMatrix()
render.setRGBA(151, 151, 151, 220)
render.setFont(fontArial92)
local str = string.rep("0", 3 - #tostring(CAR_SPEED)) .. CAR_SPEED
render.setRGBA(51, 51, 51, 256)
for k = 1, 3 do
local num = string.sub(str, k, k)
if num ~= "0" then
render.setRGBA(255, 255, 255, 250)
end
render.drawText(linesposx - 60 + k * 46, resy - 130 - 80, num)
end
render.setFont(fontArial46)
render.setRGBA(200, 51, 51, 256)
local t = "N"
if GEARBOX_GEAR == -1 then
t = "R"
elseif GEARBOX_GEAR == 0 then
t = "N"
else
t = GEARBOX_GEAR
end
render.drawText(linesposx - 35 + 164, resy - 130 - 40, t)
end)
net.receive("ENGINE_RPM", function()
local rpm = net.readFloat()
if rpm then
ENGINE_RPM = rpm
end
end)
net.receive("CAR_SPEED", function()
local speed = net.readUInt(12)
if speed then
CAR_SPEED = math.clamp(speed, 0, 999)
end
end)
net.receive("GEARBOX_GEAR", function()
local gear = net.readInt(5)
if gear then
GEARBOX_GEAR = gear
end
end)

View File

@ -18,7 +18,7 @@ function Differential:initialize(vehicle, name, config)
if CLIENT then return end if CLIENT then return end
self.wireOutputs = { self.wireOutputs = {
Diff_Torque = 'number' [string.format('%s_Torque', self.name)] = 'number'
} }
self.finalDrive = config.FinalDrive or 4 self.finalDrive = config.FinalDrive or 4
@ -52,7 +52,7 @@ end
function Differential:updateWireOutputs() function Differential:updateWireOutputs()
PowertrainComponent.updateWireOutputs(self) PowertrainComponent.updateWireOutputs(self)
wire.ports.Diff_Torque = self.torque wire.ports[string.format('%s_Torque', self.name)] = self.torque
if self.outputA ~= nil then if self.outputA ~= nil then
self.outputA:updateWireOutputs() self.outputA:updateWireOutputs()
@ -110,8 +110,8 @@ function Differential:forwardStep(torque, inertia)
self.slipTorque self.slipTorque
) )
tqA = self.outputA:forwardStep(tqA, inertia * 0.5 * math.pow(self.finalDrive, 2) + aI) / self.finalDrive tqA = self.outputA:forwardStep(tqA, inertia * 0.5 * math.pow(self.finalDrive, 2) + aI)
tqB = self.outputB:forwardStep(tqB, inertia * 0.5 * math.pow(self.finalDrive, 2) + bI) / self.finalDrive tqB = self.outputB:forwardStep(tqB, inertia * 0.5 * math.pow(self.finalDrive, 2) + bI)
-- // REFACTOR -- // REFACTOR
if self.steerLock ~= 0 then if self.steerLock ~= 0 then
@ -136,7 +136,7 @@ function Differential:forwardStep(torque, inertia)
local maxMz = math.max(self.outputA.customWheel.mz + self.outputB.customWheel.mz) local maxMz = math.max(self.outputA.customWheel.mz + self.outputB.customWheel.mz)
local minMz = math.min(self.outputA.customWheel.mz + self.outputB.customWheel.mz) local minMz = math.min(self.outputA.customWheel.mz + self.outputB.customWheel.mz)
local steerTorque = mzDiff * -1 + inputTorque local steerTorque = mzDiff / 2 * -1 + inputTorque
local steerAngularAccel = steerTorque / steerInertia local steerAngularAccel = steerTorque / steerInertia
@ -157,7 +157,7 @@ function Differential:forwardStep(torque, inertia)
self.outputB.customWheel.steerAngle = innerAngle self.outputB.customWheel.steerAngle = innerAngle
end end
return (tqA + tqB) / self.finalDrive return tqA + tqB
end end
function Differential:_openDiffTorqueSplit(tq, aW, bW, aI, bI, biasAB, preload, stiffness, powerRamp, coastRamp, slipTorque) function Differential:_openDiffTorqueSplit(tq, aW, bW, aI, bI, biasAB, preload, stiffness, powerRamp, coastRamp, slipTorque)
@ -165,17 +165,26 @@ function Differential:_openDiffTorqueSplit(tq, aW, bW, aI, bI, biasAB, preload,
end end
function Differential:_lockingDiffTorqueSplit(tq, aW, bW, aI, bI, biasAB, preload, stiffness, powerRamp, coastRamp, slipTorque) function Differential:_lockingDiffTorqueSplit(tq, aW, bW, aI, bI, biasAB, preload, stiffness, powerRamp, coastRamp, slipTorque)
local sumI = aI + bI Ta = tq * (1 - biasAB);
local w = aI / sumI * aW + bI / sumI * bW Tb = tq * biasAB;
local aTqCorr = (w - aW) * aI -- / dt
aTqCorr = aTqCorr * stiffness
local bTqCorr = (w - bW) * bI -- / dt local syncTorque = (aW - bW) * stiffness * (aI + bI) * 0.5 / TICK_INTERVAL;
bTqCorr = bTqCorr * stiffness
local biasA = math.clamp(0.5 + (bW - aW) * 0.1 * stiffness, 0, 1) Ta = Ta - syncTorque;
Tb = Tb + syncTorque;
return tq * biasA + aTqCorr, tq * (1 - biasA) * bTqCorr return Ta, Tb
-- local sumI = aI + bI
-- local w = aI / sumI * aW + bI / sumI * bW
-- local aTqCorr = (w - aW) * aI -- / dt
-- aTqCorr = aTqCorr * stiffness
-- local bTqCorr = (w - bW) * bI -- / dt
-- bTqCorr = bTqCorr * stiffness
-- local biasA = math.clamp(0.5 + (bW - aW) * 0.1 * stiffness, 0, 1)
-- return tq * biasA + aTqCorr, tq * (1 - biasA) * bTqCorr
end end
function Differential:_VLSDTorqueSplit(tq, aW, bW, aI, bI, biasAB, preload, stiffness, powerRamp, coastRamp, slipTorque) function Differential:_VLSDTorqueSplit(tq, aW, bW, aI, bI, biasAB, preload, stiffness, powerRamp, coastRamp, slipTorque)

View File

@ -32,7 +32,6 @@ function Engine:initialize(vehicle, name, config)
self.limiterDuration = config.LimiterDuration or 0.05 self.limiterDuration = config.LimiterDuration or 0.05
self.torqueMap = config.TorqueMap or {} self.torqueMap = config.TorqueMap or {}
self.rpmFrac = 0
self.friction = 0 self.friction = 0
self.masterThrottle = 0 self.masterThrottle = 0
@ -93,7 +92,7 @@ function Engine:_generateTorque()
return self.torque return self.torque
end end
function Engine:forwardStep() function Engine:forwardStep(torque, inertia)
if self.output == nil then if self.output == nil then
local generatedTorque = self:_generateTorque() local generatedTorque = self:_generateTorque()
@ -108,7 +107,7 @@ function Engine:forwardStep()
local generatedTorque = self:_generateTorque() local generatedTorque = self:_generateTorque()
local reactTorque = (targetW - self.angularVelocity) * self.inertia / TICK_INTERVAL local reactTorque = (targetW - self.angularVelocity) * self.inertia / TICK_INTERVAL
local returnedTorque = self.output:forwardStep(generatedTorque - reactTorque, self.inertia) -- ??? 0 -> self.inertia local returnedTorque = self.output:forwardStep(torque + generatedTorque - reactTorque, inertia + self.inertia)
self.reactTorque = reactTorque self.reactTorque = reactTorque
self.returnedTorque = returnedTorque self.returnedTorque = returnedTorque
@ -120,6 +119,10 @@ function Engine:forwardStep()
self.angularVelocity = self.angularVelocity + finalTorque / inertiaSum * TICK_INTERVAL self.angularVelocity = self.angularVelocity + finalTorque / inertiaSum * TICK_INTERVAL
self.angularVelocity = math.max(self.angularVelocity, 0) self.angularVelocity = math.max(self.angularVelocity, 0)
net.start("ENGINE_RPM")
net.writeFloat(self:getRPM() / self.maxRPM)
net.send(self.vehicle.playersConnectedToHUD, true)
net.start("ENGINE_FULLRPM") net.start("ENGINE_FULLRPM")
net.writeUInt(self:getRPM(), 16) net.writeUInt(self:getRPM(), 16)
net.writeFloat(self.masterThrottle) net.writeFloat(self.masterThrottle)

View File

@ -14,9 +14,9 @@ function Gearbox:initialize(vehicle, name, config)
Downshift = 'number' Downshift = 'number'
} }
self.wireOutputs = { self.wireOutputs = {
Gearbox_RPM = 'number', [string.format('%s_RPM', self.name)] = 'number',
Gearbox_Torque = 'number', [string.format('%s_Torque', self.name)] = 'number',
Gearbox_Ratio = 'number' [string.format('%s_Ratio', self.name)] = 'number'
} }
self.type = config.Type or 'MANUAL' self.type = config.Type or 'MANUAL'
@ -28,9 +28,9 @@ end
function Gearbox:updateWireOutputs() function Gearbox:updateWireOutputs()
PowertrainComponent.updateWireOutputs(self) PowertrainComponent.updateWireOutputs(self)
wire.ports.Gearbox_RPM = self:getRPM() wire.ports[string.format('%s_RPM', self.name)] = self:getRPM()
wire.ports.Gearbox_Torque = self.torque wire.ports[string.format('%s_Torque', self.name)] = self.torque
wire.ports.Gearbox_Ratio = self.ratio wire.ports[string.format('%s_Ratio', self.name)] = self.ratio
end end
function Gearbox:queryInertia() function Gearbox:queryInertia()
@ -52,18 +52,18 @@ function Gearbox:queryAngularVelocity(angularVelocity)
end end
function Gearbox:forwardStep(torque, inertia) function Gearbox:forwardStep(torque, inertia)
self.torque = torque * self.ratio
if self.output == nil then if self.output == nil then
return torque return torque
end end
if self.ratio == 0 then if self.ratio == 0 then
self.output:forwardStep(0, self.inertia) self.output:forwardStep(0, self.inertia * 0.5)
return torque return torque
end end
self.torque = torque * self.ratio
return self.output:forwardStep(self.torque, (inertia + self.inertia) * math.pow(self.ratio, 2)) / self.ratio return self.output:forwardStep(self.torque, (inertia + self.inertia) * math.pow(self.ratio, 2)) / self.ratio
end end

View File

@ -1,9 +1,11 @@
--@include /koptilnya/libs/watcher.txt --@include /koptilnya/libs/watcher.txt
--@include /koptilnya/libs/utils.txt
--@include ./base.txt --@include ./base.txt
local BaseGearbox = require('./base.txt') local BaseGearbox = require('./base.txt')
require('/koptilnya/libs/watcher.txt') require('/koptilnya/libs/watcher.txt')
require('/koptilnya/libs/utils.txt')
local ManualGearbox = class('ManualGearbox', BaseGearbox) local ManualGearbox = class('ManualGearbox', BaseGearbox)
@ -13,7 +15,7 @@ function ManualGearbox:initialize(vehicle, name, config)
if CLIENT then return end if CLIENT then return end
table.merge(self.wireOutputs, { table.merge(self.wireOutputs, {
Gearbox_Gear = 'number' [string.format('%s_Gear', self.name)] = 'number'
}) })
self.ratios = config.Ratios or { 3.6, 2.2, 1.5, 1.2, 1.0, 0.8} self.ratios = config.Ratios or { 3.6, 2.2, 1.5, 1.2, 1.0, 0.8}
@ -22,6 +24,8 @@ function ManualGearbox:initialize(vehicle, name, config)
self.gear = 0 self.gear = 0
function shiftFunc() function shiftFunc()
if wire.ports.Clutch == 0 then return 0 end
local upshift = wire.ports.Upshift or 0 local upshift = wire.ports.Upshift or 0
local downshift = wire.ports.Downshift or 0 local downshift = wire.ports.Downshift or 0
@ -40,13 +44,17 @@ end
function ManualGearbox:updateWireOutputs() function ManualGearbox:updateWireOutputs()
BaseGearbox.updateWireOutputs(self) BaseGearbox.updateWireOutputs(self)
wire.ports.Gearbox_Gear = self.gear wire.ports[string.format('%s_Gear', self.name)] = self.gear
end end
function ManualGearbox:setGear(gear) function ManualGearbox:setGear(gear)
if gear >= -1 and gear <= #self.ratios then if gear >= -1 and gear <= #self.ratios then
self.gear = gear self.gear = gear
self:recalcRatio() self:recalcRatio()
net.start('GEARBOX_GEAR')
net.writeInt(gear, 5)
net.send(self.vehicle.playersConnectedToHUD, true)
end end
end end
@ -65,10 +73,10 @@ function ManualGearbox:shift(dir)
end end
function ManualGearbox:forwardStep(torque, inertia) function ManualGearbox:forwardStep(torque, inertia)
local result = BaseGearbox.forwardStep(self, torque, inertia)
self.shiftWatcher() self.shiftWatcher()
local result = BaseGearbox.forwardStep(self, torque, inertia)
return result return result
end end

View File

@ -25,20 +25,21 @@ function PowertrainComponent:initialize(vehicle, name, config)
self.angularVelocity = 0 self.angularVelocity = 0
self.torque = 0 self.torque = 0
self.DEBUG_DATA = {}
if self.DEBUG then if self.DEBUG then
if CLIENT then if CLIENT then
self.DEBUG_DATA = {}
net.receive('DEBUG_' .. self.name, function() net.receive('DEBUG_' .. self.name, function()
self.DEBUG_DATA = net.readTable() self:deserializeDebugData(self.DEBUG_DATA)
end) end)
end end
self.DEBUG_SEND_DATA_DEBOUNCED = debounce(function() self.DEBUG_SEND_DATA_DEBOUNCED = debounce(function()
net.start("DEBUG_" .. self.name) net.start("DEBUG_" .. self.name)
net.writeTable(self.DEBUG_DATA) self:serializeDebugData()
net.send(nil, true) net.send(self.vehicle.playersConnectedToHUD, true)
end, TICK_INTERVAL * 20) end, 1 / 10)
end end
end end
@ -90,8 +91,14 @@ function PowertrainComponent:updateWireOutputs()
WireComponent.updateWireOutputs(self) WireComponent.updateWireOutputs(self)
if self.DEBUG then if self.DEBUG then
self.DEBUG_SEND_DATA_DEBOUNCED() -- self:DEBUG_SEND_DATA_DEBOUNCED()
end end
end end
function PowertrainComponent:serializeDebugData()
end
function PowertrainComponent:deserializeDebugData(result)
end
return PowertrainComponent return PowertrainComponent

View File

@ -21,6 +21,9 @@ function Wheel:initialize(vehicle, name, config)
local font = render.createFont("Roboto", 256, 400, true) local font = render.createFont("Roboto", 256, 400, true)
local mat = render.createMaterial('models/debug/debugwhite') local mat = render.createMaterial('models/debug/debugwhite')
local lerpLoad = 0
local lerpForwardFrictionForce = 0
local lerpSideFrictionForce = 0
hook.add("PostDrawTranslucentRenderables", "DEBUG_RENDER_" .. self.name, function() hook.add("PostDrawTranslucentRenderables", "DEBUG_RENDER_" .. self.name, function()
if next(self.DEBUG_DATA) == nil then return end if next(self.DEBUG_DATA) == nil then return end
if not isValid(self.DEBUG_DATA.entity) then return end if not isValid(self.DEBUG_DATA.entity) then return end
@ -29,16 +32,19 @@ function Wheel:initialize(vehicle, name, config)
render.setMaterial(mat) render.setMaterial(mat)
render.setColor(Color(255, 0, 0, 200)) render.setColor(Color(255, 0, 0, 200))
render.draw3DBeam(pos, pos + (16 * self.DEBUG_DATA.forward), 1, 0, 0) lerpForwardFrictionForce = math.lerp(0.1, lerpForwardFrictionForce, self.DEBUG_DATA.forwardFrictionForce)
render.draw3DBeam(pos, pos + ((lerpForwardFrictionForce / 500 + 16) * self.DEBUG_DATA.forward), 1, 0, 0)
render.setColor(Color(0, 255, 0, 255)) render.setColor(Color(0, 255, 0, 255))
render.draw3DBeam(pos, pos + (16 * self.DEBUG_DATA.right), 1, 0, 0) lerpSideFrictionForce = math.lerp(0.1, lerpSideFrictionForce, self.DEBUG_DATA.sideFrictionForce)
render.draw3DBeam(pos, pos + ((lerpSideFrictionForce / 500 + 16) * self.DEBUG_DATA.right), 1, 0, 0)
render.setColor(Color(0, 0, 255, 200)) render.setColor(Color(0, 0, 255, 200))
render.draw3DBeam(pos, pos + (16 * self.DEBUG_DATA.up), 1, 0, 0) lerpLoad = math.lerp(0.1, lerpLoad, self.DEBUG_DATA.load)
render.draw3DBeam(pos, pos + ((lerpLoad * 4 + 16) * self.DEBUG_DATA.up), 1, 0, 0)
end) end)
if player() == owner() then if player() == owner() and not render.isHUDActive() then
enableHud(nil, true) enableHud(nil, true)
end end
@ -56,8 +62,7 @@ function Wheel:initialize(vehicle, name, config)
} }
self.wireOutputs = { self.wireOutputs = {
[string.format('%s_RPM', self.name)] = 'number', [string.format('%s_RPM', self.name)] = 'number',
[string.format('%s_Mz', self.name)] = 'number', [string.format('%s_W', self.name)] = 'number'
[string.format('%s_Fz', self.name)] = 'number',
} }
self.rot = 0 self.rot = 0
@ -79,10 +84,6 @@ function Wheel:initialize(vehicle, name, config)
self.holo = self:createHolo(self.entity) self.holo = self:createHolo(self.entity)
if self.DEBUG then
self.DEBUG_DATA.entity = self.entity
end
if not config.Radius then if not config.Radius then
self.customWheel.radius = self:getEntityRadius() self.customWheel.radius = self:getEntityRadius()
end end
@ -122,8 +123,7 @@ function Wheel:updateWireOutputs()
PowertrainComponent.updateWireOutputs(self) PowertrainComponent.updateWireOutputs(self)
wire.ports[string.format('%s_RPM', self.name)] = self.customWheel:getRPM() wire.ports[string.format('%s_RPM', self.name)] = self.customWheel:getRPM()
wire.ports[string.format('%s_Mz', self.name)] = self.customWheel.mz wire.ports[string.format('%s_W', self.name)] = self.customWheel.angularVelocity
wire.ports[string.format('%s_Fz', self.name)] = self.customWheel.load
end end
function Wheel:queryInertia() function Wheel:queryInertia()
@ -139,12 +139,12 @@ function Wheel:forwardStep(torque, inertia)
return 0 return 0
end end
self.customWheel.motorTorque = torque * self.direction self.customWheel.motorTorque = torque
self.customWheel.brakeTorque = math.max(self.brakePower * self.vehicle.brake, self.handbrakePower * self.vehicle.handbrake) self.customWheel.brakeTorque = math.max(self.brakePower * self.vehicle.brake, self.handbrakePower * self.vehicle.handbrake)
self.customWheel:update() self.customWheel:update()
self.angularVelocity = self.customWheel.angularVelocity * self.direction self.angularVelocity = self.customWheel.angularVelocity
if self.customWheel.hasHit and isValid(self.vehicle.basePhysObject) then if self.customWheel.hasHit and isValid(self.vehicle.basePhysObject) then
local surfaceForceVector = self.customWheel.right * self.customWheel.sideFriction.force + self.customWheel.forward * self.customWheel.forwardFriction.force local surfaceForceVector = self.customWheel.right * self.customWheel.sideFriction.force + self.customWheel.forward * self.customWheel.forwardFriction.force
@ -153,18 +153,12 @@ function Wheel:forwardStep(torque, inertia)
end end
if isValid(self.holo) then if isValid(self.holo) then
if self.DEBUG then
self.DEBUG_DATA.forward = self.customWheel.forward
self.DEBUG_DATA.right = self.customWheel.right
self.DEBUG_DATA.up = self.customWheel.up
end
local entityAngles = self.entity:getAngles() local entityAngles = self.entity:getAngles()
self.rot = self.rot + self.angularVelocity * TICK_INTERVAL * self.direction self.rot = self.rot + self.angularVelocity * TICK_INTERVAL
local steerRotated = entityAngles:rotateAroundAxis(self.customWheel.up, nil, math.rad(-self.customWheel.steerAngle) + self.customWheel.toeAngle) local steerRotated = entityAngles:rotateAroundAxis(self.customWheel.up, nil, math.rad(-self.customWheel.steerAngle) + self.customWheel.toeAngle)
local camberRotated = steerRotated:rotateAroundAxis(self.customWheel.forward, nil, -self.customWheel.camberAngle) local camberRotated = steerRotated:rotateAroundAxis(self.customWheel.forward, nil, -self.customWheel.camberAngle * self.direction)
local angularVelocityRotated = camberRotated:rotateAroundAxis(self.customWheel.right, nil, self.rot) local angularVelocityRotated = camberRotated:rotateAroundAxis(self.customWheel.right, nil, self.rot)
self.holo:setAngles(angularVelocityRotated) self.holo:setAngles(angularVelocityRotated)
@ -173,4 +167,26 @@ function Wheel:forwardStep(torque, inertia)
return self.customWheel.counterTorque return self.customWheel.counterTorque
end end
function Wheel:serializeDebugData()
net.writeEntity(self.entity)
net.writeVector(self.customWheel.forward)
net.writeVector(self.customWheel.right)
net.writeVector(self.customWheel.up)
net.writeFloat(self.customWheel.load)
net.writeFloat(self.customWheel.forwardFriction.force)
net.writeFloat(self.customWheel.sideFriction.force)
end
function Wheel:deserializeDebugData(result)
net.readEntity(function(ent)
result.entity = ent
end)
result.forward = net.readVector()
result.right = net.readVector()
result.up = net.readVector()
result.load = net.readFloat()
result.forwardFrictionForce = net.readFloat()
result.sideFrictionForce = net.readFloat()
end
return Wheel return Wheel

View File

@ -22,7 +22,7 @@ function Vehicle:initialize(config)
self.config = config self.config = config
self.components = {} self.components = {}
self.independentComponents = {} self.headComponents = {}
self:createComponents() self:createComponents()
self:linkComponents() self:linkComponents()
@ -31,21 +31,32 @@ function Vehicle:initialize(config)
self:createIO() self:createIO()
end end
self.rootComponent = self:getRootComponent()
if SERVER then if SERVER then
self.steer = 0 self.steer = 0
self.brake = 0 self.brake = 0
self.handbrake = self.handbrake = 0
self.playersConnectedToHUD = {}
hook.add('input', 'vehicle_wire_input', function(name, value) hook.add('input', 'vehicle_wire_input', function(name, value)
self:handleWireInput(name, value) self:handleWireInput(name, value)
end) end)
end
hook.add('tick', 'vehicle_update', function() hook.add('tick', 'vehicle_update', function()
self:update() self:update()
end) end)
hook.add('HUDConnected', 'vehicle_hudconnected', function(ent, ply)
table.insert(self.playersConnectedToHUD, ply)
end)
hook.add('HUDDisconnected', 'vehicle_huddisconnected', function(ent, ply)
table.removeByValue(self.playersConnectedToHUD, ply)
end)
else
--@include ./hud.txt
require("./hud.txt")
end
end end
function Vehicle.static:validateConfig(config) function Vehicle.static:validateConfig(config)
@ -70,8 +81,8 @@ function Vehicle:linkComponents()
for _, componentConfig in pairs(self.config) do for _, componentConfig in pairs(self.config) do
local component = self:getComponentByName(componentConfig.Name) local component = self:getComponentByName(componentConfig.Name)
if componentConfig.Input == nil && component ~= self:getRootComponent() then if componentConfig.Input == nil then
table.insert(self.independentComponents, component) table.insert(self.headComponents, component)
else else
local inputComponent = self:getComponentByName(componentConfig.Input) local inputComponent = self:getComponentByName(componentConfig.Input)
@ -122,15 +133,17 @@ function Vehicle:update()
if SERVER then if SERVER then
local base = wire.ports.Base local base = wire.ports.Base
self.rootComponent:forwardStep() for _, component in pairs(self.headComponents) do
component:forwardStep(0, 0)
for _, component in pairs(self.independentComponents) do
component:forwardStep(0, component:queryInertia())
end end
for _, component in pairs(self.components) do for _, component in pairs(self.components) do
component:updateWireOutputs() component:updateWireOutputs()
end end
-- net.start("CAR_SPEED")
-- net.writeUInt(base:getVelocity():getLength()* 1.905 / 100000 * 3600, 12)
-- net.send(self.playersConnectedToHUD, true)
end end
--if isValid(base) and (self.clutch:getPress() == 1 or self.gearbox.ratio == 0) then --if isValid(base) and (self.clutch:getPress() == 1 or self.gearbox.ratio == 0) then

View File

@ -3,10 +3,10 @@ local FrictionPreset = class('FrictionPreset')
function FrictionPreset:initialize(config) function FrictionPreset:initialize(config)
config = config or {} config = config or {}
self.B = config.B or 11 self.B = config.B or 10.86
self.C = config.C or 1.15 self.C = config.C or 2.15
self.D = config.D or 1.03 self.D = config.D or 0.933
self.E = config.E or -10 self.E = config.E or 0.992
end end
function FrictionPreset:evaluate(slip) function FrictionPreset:evaluate(slip)

View File

@ -201,7 +201,7 @@ function Wheel:selfAligningTorque(Sy, load)
return Mz return Mz
end end
function Wheel:rotateVector(vector) function Wheel:rotateVector(vector, jopa)
local ang = self.entity:getAngles() local ang = self.entity:getAngles()
local baseForward = ang:getForward() local baseForward = ang:getForward()
local baseUp = ang:getUp() local baseUp = ang:getUp()
@ -229,9 +229,9 @@ function Wheel:update()
self.lateralLoadCoefficient = self:getLateralLoadCoefficient(self.load * 1000) self.lateralLoadCoefficient = self:getLateralLoadCoefficient(self.load * 1000)
local ang = self.entity:getAngles() local ang = self.entity:getAngles()
local baseForward = ang:getForward() local baseForward = ang:getForward() * self.direction
local baseUp = ang:getUp() local baseUp = ang:getUp()
local baseRight = -ang:getRight() local baseRight = -ang:getRight() * self.direction
self.forward = self:rotateVector(baseForward) self.forward = self:rotateVector(baseForward)
self.right = self:rotateVector(baseRight) self.right = self:rotateVector(baseRight)

View File

@ -59,7 +59,7 @@ function Sound:initialize(redline, parent, sounds)
smoothThrottle = smoothThrottle * (1 - 0.1) + throttle * 0.1 smoothThrottle = smoothThrottle * (1 - 0.1) + throttle * 0.1
for n, rpm in ipairs(soundRpms) do for n, rpm in ipairs(soundRpms) do
if not soundObjects[rpm] then if not soundObjects[rpm] or not soundObjects[rpm].Bass then
goto CONTINUE goto CONTINUE
end end
local min = n == 1 and -100000 or soundRpms[n - 1] local min = n == 1 and -100000 or soundRpms[n - 1]