Skip to content

Instantly share code, notes, and snippets.

@CheezusChrust
Last active March 27, 2023 21:30
Show Gist options
  • Save CheezusChrust/114f3281fb324f14212c684fd71b3208 to your computer and use it in GitHub Desktop.
Save CheezusChrust/114f3281fb324f14212c684fd71b3208 to your computer and use it in GitHub Desktop.
A Starfall-based SetAng steering chip with decent counter-steering abilities
--@name Car Steering v3.4
--@author Cheezus
--@shared
--@model models/sprops/rectangles_thin/size_2/rect_12x12x1_5.mdl
local showAxes = true
local frontWheelPos = Vector(50, 0, 0) -- Between your front wheels
local rightAxis = Vector(0, -1, 0) -- The RIGHT facing direction of your car
local upAxis = Vector(0, 0, 1) -- The UP facing direction of your car
local fwdAxis = rightAxis:cross(-upAxis)
rightAxis:normalize()
upAxis:normalize()
if SERVER then
local maxAngle = 45 -- Maximum steering angle, in degrees
local steeringForce = 120 -- Maximum steering force, affects steering rate and how much your inputs overpower the countersteer
local casterAngle = 0 -- Caster angle, in degrees
local slipCounterForce = 1 -- Stops you from slipping sideways
local gyroCounterForce = 1 -- Stops you from rotating, tries to maintain current angle
local counterMax = 180 -- Maximum counter force, can be completely overpowered by manual inputs if lower than steeringForce
local antiWobbleAngle = 1.5 -- Lower than this angle, multiply the current steering angle by the value below every tick
local antiWobbleMul = 0.25 -- This helps cars that have a tendency to wobble while driving straight at high speeds
local addRateMul = 0 -- Increases manual steering power based on velocity, 0 to disable, increase in increments of 1-2
local interval = 2 -- Run every X ticks
wire.adjustPorts({
Base = "entity",
A = "number",
D = "number",
Active = "number"
}, {
SteeringAngle = "number",
Slip = "number",
VelL = "vector",
Understeer = "number",
Oversteer = "number"
})
local clamp = math.clamp
local abs = math.abs
local chip = chip()
local dt = game.getTickInterval() * interval
net.receive("requestBase", function(_, ply)
if not wire.ports.Base:isValid() then return end
net.start("sendBase")
net.writeEntity(wire.ports.Base)
net.send(ply)
end)
chip:setCollisionGroup(COLLISION_GROUP.WORLD)
local steeringAngle = 0
local tickCount = 0
hook.add("tick", "interval", function()
local active = wire.ports.Active > 0
tickCount = tickCount + 1
if tickCount % (active and interval or 10) ~= 0 then
return
else
tickCount = 0
end
local base = wire.ports.Base
if not base:isValid() then return end
local localUpAxis = base:localToWorldVector(upAxis)
if active then
local a = wire.ports.A
local d = wire.ports.D
local fwpos = base:localToWorld(frontWheelPos)
local velL = base:worldToLocal(base:getPhysicsObject():getVelocityAtPoint(fwpos) + base:getPos()) * dt
local slip = velL:rotateAroundAxis(localUpAxis, -steeringAngle):dot(rightAxis)
wire.ports.Understeer = slip / velL:getLength()
wire.ports.Slip = slip
wire.ports.VelL = velL
slip = slip * slipCounterForce
local rearPos = -frontWheelPos * -upAxis
local rearVel = base:worldToLocal(base:getPhysicsObject():getVelocityAtPoint(rearPos) + base:getPos())
wire.ports.Oversteer = rearVel:dot(rightAxis) / rearVel:getLength()
local yawDamping = base:getAngleVelocity():dot(localUpAxis) * dt
--yawDamping = yawDamping * upAxis.z
yawDamping = yawDamping * gyroCounterForce
local addRate = velL:getLength() * addRateMul
local turn = (a - d) * (steeringForce + addRate) * dt
local counter = clamp(slip + yawDamping, -counterMax, counterMax)
steeringAngle = clamp(steeringAngle + turn - counter, -maxAngle, maxAngle)
if (abs(steeringAngle) < antiWobbleAngle) and (a == 0 and d == 0) then
steeringAngle = steeringAngle * antiWobbleMul
end
else
steeringAngle = 0
end
local casterAxis = base:localToWorldVector(fwdAxis):rotateAroundAxis(localUpAxis, steeringAngle)
local casterRotation = steeringAngle / maxAngle * casterAngle
local finalAngle = base:getAngles():rotateAroundAxis(localUpAxis, steeringAngle):rotateAroundAxis(casterAxis, -casterRotation)
chip:setAngles(finalAngle)
wire.ports.SteeringAngle = steeringAngle
end)
timer.create("freezer", 0.5, 0, function()
if not chip:isPlayerHolding() and not chip:isFrozen() then
chip:setFrozen(true)
end
end)
elseif player() == owner() then
if not showAxes then return end
local base
timer.create("requestBase", 0.5, 0, function()
net.start("requestBase")
net.send()
end)
net.receive("sendBase", function()
base = net.readEntity()
timer.stop("requestBase")
end)
enableHud(owner(), true)
local font = render.createFont("Trebuchet24", 24, nil, false, nil, nil, true)
hook.add("drawHud", "hud", function()
if not base or not base:isValid() then return end
local basePos = base:localToWorld(frontWheelPos)
local right = base:localToWorld(frontWheelPos + rightAxis * 50)
local up = base:localToWorld(frontWheelPos + upAxis * 50)
local fwd = base:localToWorld(frontWheelPos + fwdAxis * 50)
local alpha = math.max(300 - owner():getPos():getDistanceSqr(basePos) / 1000, 0)
if alpha > 0 then
render.pushViewMatrix({
type = "3D"
})
render.setColor(Color(255, 0, 0))
render.draw3DLine(basePos, right)
render.setColor(Color(0, 255, 0))
render.draw3DLine(basePos, up)
render.setColor(Color(0, 0, 255))
render.draw3DLine(basePos, fwd)
render.popViewMatrix()
local rightScr = right:toScreen()
local upScr = up:toScreen()
local fwdScr = fwd:toScreen()
render.setFont(font)
render.setColor(Color(255, 0, 0, alpha))
render.drawSimpleText(rightScr.x, rightScr.y, "Right", 1, 1)
render.setColor(Color(0, 255, 0, alpha))
render.drawSimpleText(upScr.x, upScr.y, "Up", 1, 1)
render.setColor(Color(0, 0, 255, alpha))
render.drawSimpleText(fwdScr.x, fwdScr.y, "Forward", 1, 1)
end
end)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment