Skip to content

Instantly share code, notes, and snippets.

@CheezusChrust
Last active February 15, 2023 02:01
Show Gist options
  • Save CheezusChrust/275de37bf5058aa14d558d271f45c74d to your computer and use it in GitHub Desktop.
Save CheezusChrust/275de37bf5058aa14d558d271f45c74d to your computer and use it in GitHub Desktop.
A simple simulated gearbox
--@name Simulated Gearbox
--@author Cheezus
--@shared
local crankAxis = Vector(0, 0, 1) -- Rotation axis of the crankshaft
local flywheelAxis = Vector(0, 0, 1) -- Rotation axis of the flywheel
crankAxis:normalize()
flywheelAxis:normalize()
if SERVER then
local ratios = {
-8,
0,
8,
5,
2.5,
1
}
local finalDrive = 1 -- All ratios get multiplied by this number
local gear = 1 -- Gear to start in
local holdingForce = 0 -- Force for holding the crank and flywheel angles EXACTLY together
-- 0 will essentially act as a centrifugal clutch (and is more stable)
local angVelMatchForce = 0.2 -- Force for matching the angular velocity between crank and flywheel
-- This allows some slipping between the two linked entities
wire.adjustPorts({
GearUp = "number",
GearDown = "number",
Crank = "entity",
Flywheel = "entity"
}, {
CrankRPM = "number",
FlywheelRPM = "number",
Ratio = "number",
Gear = "number"
})
local crank = wire.ports.Crank
local flywheel = wire.ports.Flywheel
local abs = math.abs
local min = math.min
local function sendEnts()
net.start("send")
net.writeEntity(crank)
net.writeEntity(flywheel)
net.send(owner())
end
hook.add("input", "ports", function(name, value)
if name == "GearUp" and value > 0 and gear < #ratios then
gear = gear + 1
wire.ports.Gear = gear
end
if name == "GearDown" and value > 0 and gear > 1 then
gear = gear - 1
wire.ports.Gear = gear
end
if name == "Crank" and value:isValid() then
crank = value
if isValid(flywheel) then sendEnts() end
end
if name == "Flywheel" and value:isValid() then
flywheel = value
if isValid(crank) then sendEnts() end
end
end)
-- No negative axes or everything breaks
for k, v in ipairs(crankAxis) do
crankAxis[k] = abs(v)
end
for k, v in ipairs(flywheelAxis) do
flywheelAxis[k] = abs(v)
end
local angularError = 0
hook.add("tick", "simulate", function()
if not isValid(crank) or not isValid(flywheel) then return end
local crankAngVel = crank:getAngleVelocity():dot(crankAxis)
local flywheelAngVel = flywheel:getAngleVelocity():dot(flywheelAxis)
wire.ports.CrankRPM = abs(crankAngVel / 6)
wire.ports.FlywheelRPM = abs(flywheelAngVel / 6)
local ratio = ratios[gear] * finalDrive
wire.ports.Ratio = ratio
if ratio == 0 then return end
-- This should only be calculated on gear changes, not every tick
local lowestInertia = min(
crank:getInertia():dot(crankAxis),
flywheel:getInertia():dot(flywheelAxis) / (ratio ^ 2)
)
local angVelDifference = flywheelAngVel * ratio - crankAngVel
angularError = angularError + angVelDifference
wire.ports.AngVelDiff = angVelDifference
wire.ports.AngError = angularError
local force = Vector((holdingForce * angularError + angVelDifference * angVelMatchForce) * lowestInertia)
crank:applyTorque(crank:localToWorldVector(force * crankAxis))
flywheel:applyTorque(flywheel:localToWorldVector(-force * flywheelAxis * ratio))
end)
net.receive("request", function()
if not isValid(crank) or not isValid(flywheel) then return end
sendEnts()
end)
elseif player() == owner() then
timer.create("requestEnts", 1, 0, function()
net.start("request")
net.send()
end)
local crank
local flywheel
net.receive("send", function()
crank = net.readEntity()
flywheel = net.readEntity()
timer.remove("requestEnts")
end)
enableHud(owner(), true)
hook.add("drawhud", "hud", function()
if not isValid(crank) or not isValid(flywheel) then return end
if not owner():isAlive() then return end
if owner():getActiveWeapon():getClass() ~= "gmod_tool" then return end
render.pushViewMatrix({
type = "3D"
})
local t = owner():getEyeTrace()
if t.Entity and (t.Entity == crank or t.Entity == flywheel) then
local size = crank:obbMaxs():dot(crankAxis)
render.setColor(Color(255, 255, 255))
render.draw3DLine(crank:localToWorld(crankAxis * -size), crank:getPos())
render.setColor(Color(255, 0, 0))
render.draw3DLine(crank:getPos(), crank:localToWorld(crankAxis * size))
size = flywheel:obbMaxs():dot(flywheelAxis)
render.setColor(Color(255, 255, 255))
render.draw3DLine(flywheel:localToWorld(flywheelAxis * -size), flywheel:getPos())
render.setColor(Color(255, 0, 0))
render.draw3DLine(flywheel:getPos(), flywheel:localToWorld(flywheelAxis * size))
end
render.popViewMatrix()
end)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment