Last active
February 15, 2023 02:01
-
-
Save CheezusChrust/275de37bf5058aa14d558d271f45c74d to your computer and use it in GitHub Desktop.
A simple simulated gearbox
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--@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