VE Developer Recipes & Patterns
The comprehensive vehicle scripting reference — quick one-liners, reusable patterns, and 62+ copy-paste recipes for powertrain, physics, damage, UI, and inter-VM communication.
The comprehensive vehicle scripting reference. Quick one-liners at the top, mid-length patterns in the middle, and full recipes at the bottom.
All snippets run in the VE (Vehicle Engine) context — inside lua/vehicle/extensions/ files. Use Ctrl+F to search by keyword.
Quick Reference
Copy-paste one-liners for the most common vehicle scripting tasks.
Physical State
- Get Position (World):
local pos = obj:getPosition() - Get Velocity (m/s):
local vel = obj:getVelocity() - Get Speed (km/h):
local speed = obj:getVelocity():length() * 3.6 - Get Direction (Forward):
local fwd = obj:getDirectionVector() - Get Direction (Up):
local up = obj:getDirectionVectorUp() - Get Rotation (Quat):
local rot = obj:getRotation() - Get Angular Velocity:
local angVel = obj:getAngularVelocity() - Teleport Vehicle (via GE):
obj:queueGameEngineLua(string.format("be:getObjectByID(%d):setTransform(MatrixF(quat(0,0,0,1), vec3(0,0,0)))", obj:getId())) - Apply Force (to Node):
obj:applyForceVector(nodeId, vec3(0,1000,0)) - Get Total Mass:
local mass = 0; for _, n in pairs(v.data.nodes) do mass = mass + n.nodeWeight end
Electrics & Data Bus
- Get Engine RPM:
local rpm = electrics.values.rpm or 0 - Get Throttle Input:
local throttle = electrics.values.throttle or 0 - Get Brake Input:
local brake = electrics.values.brake or 0 - Get Steering Input:
local steering = electrics.values.steering or 0 - Get Airspeed (m/s):
local airspeed = electrics.values.airspeed or 0 - Get Current Gear:
local gear = electrics.values.gear or 0 - Check Ignition State:
local ignition = electrics.values.ignitionLevel or 0-- 0=off, 1=acc, 2=on, 3=start - Toggle Hazard Lights:
electrics.setIgnitionLevel(2); electrics.toggle_warn_signal() - Get Battery Level (EV):
local fuel = electrics.values.fuel or 0 - Custom Signal:
electrics.values.mySignal = 1.0
Powertrain & Gearbox
- Shift to Gear:
controller.mainController.shiftToGearIndex(1) - Set Gearbox Mode:
controller.mainController.setGearboxMode('manual')-- 'arcade', 'realistic', 'manual' - Get Engine Load:
local engine = powertrain.getDevice("mainEngine"); local load = engine.engineLoad - Get Wheel Speed:
local speed = wheels.wheels[0].angularVelocity * wheels.wheels[0].radius - Check if Engine Running:
local isRunning = powertrain.getDevice("mainEngine").state == "running" - Kill Engine:
powertrain.getDevice("mainEngine"):lockUp() - Apply Power Multiplier:
powertrain.getDevice("mainEngine").outputTorqueState = 1.2-- +20% power - Check NOS Status:
local nos = electrics.values.n2oActive or false - Access Differential:
local diff = powertrain.getDevice("rearDiff")
Damage & State
- Get Total Damage:
local damage = beamstate.damage - Is Tire Popped:
local popped = damageTracker.getDamage("wheels", "tireFL") - Is Engine Broken:
local broken = damageTracker.getDamage("engine", "engineBlock") - Check Radiator:
local leaking = damageTracker.getDamage("engine", "radiator") - Get Body Damage %:
local fl = damageTracker.getDamage("body", "FL") or 0 - Deflate Tire:
beamstate.deflateTire(0) - Break All Hinges:
beamstate.breakHinges() - Get Part Condition:
local cond = partCondition.getConditions()["engine"] - Reset Vehicle:
onVehicleReset() - Check First Player:
local seated = playerInfo.firstPlayerSeated
UI & Audio
- UI Message (Toast):
guihooks.message("Hello World", 5, "category") - Send to Console:
print("Value: " .. tostring(val)) - Play Sound (Once on Node):
obj:playSFXOnce("event:>Vehicle>Failures>tire_burst", 0, 1, 1)-- event, nodeId, volume, pitch - Trigger UI Hook:
guihooks.trigger("myEvent", {data = 1}) - Update UI Stream:
guihooks.queueStream("myValue", 100) - Draw Line (Debug):
obj.debugDrawProxy:drawLine(pos1, pos2, color) - Draw Text (Debug):
obj.debugDrawProxy:drawText(pos, color, "Hello") - Draw Sphere (Debug):
obj.debugDrawProxy:drawSphere(pos, 0.5, color)
Inter-VM & Logic
- Call Game Engine:
obj:queueGameEngineLua("print('Hello from VE')") - Call Other Vehicle:
obj:queueObjectLuaCommand(targetId, "print('Hello from Peer')") - Set Timer:
local timer = HighPerfTimer() - Stop/Reset Timer:
local ms = timer:stopAndReset()
Common Patterns
Patterns you'll use repeatedly in vehicle extensions. Each one solves a specific, common problem with production-quality code.
High-G Impact Detection
Detecting when a vehicle has hit something hard, regardless of structural damage.
local impactThreshold = 50 -- G's
local function updateGFX(dt)
local gX = sensors.gx or 0
local gY = sensors.gy or 0
local gZ = sensors.gz or 0
local totalG = math.sqrt(gX*gX + gY*gY + gZ*gZ) / 9.81
if totalG > impactThreshold then
guihooks.message("Impact Detected!", 2, "safety")
end
endSpeed-Based Conditional Logic
Automatically performing an action (like deploying a spoiler) at a specific speed.
local deploySpeed = 80 / 3.6 -- 80 km/h in m/s
local function updateGFX(dt)
local speed = electrics.values.airspeed or 0
if speed > deploySpeed then
electrics.values.spoilerPosition = 1.0
else
electrics.values.spoilerPosition = 0.0
end
endMonitoring Damage Delta
Detecting a new collision by checking the change in total damage energy.
local lastDamage = 0
local function updateGFX(dt)
local currentDamage = beamstate.damage or 0
if currentDamage > lastDamage then
local delta = currentDamage - lastDamage
print("Vehicle sustained " .. delta .. " Joules of new damage!")
lastDamage = currentDamage
end
endCross-Module Communication via electrics
Using the electrics bus to decouple a custom controller from a visual prop.
-- In your custom extension or controller:
local function updateGFX(dt)
electrics.values.customNeedle = math.sin(os.clock())
end
-- In the JBeam (visual prop section), the prop is defined to watch "customNeedle"
-- ["customNeedle", "needle_mesh_name", "rotation_axis", ...]Throttled Logic Updates
Running expensive logic at a lower frequency than the frame rate.
local timer = 0
local interval = 0.5 -- 2Hz (runs twice per second)
local function updateGFX(dt)
timer = timer + dt
if timer >= interval then
if obj:inWater(0) then
print("Underwater!")
end
timer = 0
end
endRemote Vehicle State Sync
Sending your speed to another vehicle for coordinated behavior (like a convoy).
local function updateGFX(dt)
local mySpeed = electrics.values.airspeed or 0
local targetVehId = 1234
obj:queueObjectLuaCommand(targetVehId, string.format("convoy.updateLeadSpeed(%f)", mySpeed))
endFull Recipes
Numbered recipes for quick reference. Each one is a self-contained solution you can drop into your vehicle extension.
1. Powertrain & Torque
- Add permanent torque boost (In
onInit):for rpm, torque in pairs(eng.torqueCurve) do if type(rpm) == "number" then eng.torqueCurve[rpm] = torque * 1.2 end end; eng.torqueData = eng:getTorqueData() - Find the main engine object:
local eng = powertrain.getDevice("mainEngine") - Check if clutch is slipping:
local isSlipping = math.abs(eng.outputAV1 - gearbox.inputAV) > 1 - Force a specific gear:
controller.mainController.shiftToGearIndex(2) - Disable/Reset rev limiter:
eng:resetTempRevLimiter() - Calculate current Horsepower:
local hp = (eng.combustionTorque * eng.outputAV1) / 745.7 - Stall/Lock engine:
powertrain.getDevice("mainEngine"):lockUp() - Check fuel level percentage:
local fuel = electrics.values.fuel * 100 - Change idle RPM dynamically:
eng.idleAVOverwrite = 800 * 0.104719755 - Detect when shifting starts:
if electrics.values.isShifting then ... end - Architectural Boost (UI Visible): Override
getTorqueDatato multiply curve values (see Modding Guide). - Master Power Multiplier:
eng.outputTorqueState = 0.5(50% power) - Get total wheel torque:
local totalTorque = wheels.wheelTorque - Check if engine is running:
local running = not eng.isStalled and eng.outputAV1 > 0 - Apply braking torque to all wheels:
wheels.scaleBrakeTorque(1.0) - Get turbo boost pressure:
local boost = electrics.values.turboBoost or 0 - Check if NOS is active:
local nos = electrics.values.n2oActive or false
2. JBeam & Physics
- Get JBeam variable value:
local myVar = v.data.variables["$myVar"] - Change beam spring/damp at runtime:
obj:setBeam(cid, id1, id2, strength, spring, damp) - Find node CID by name:
local nodeId = beamstate.nodeNameMap["nodeName"] - Get node world position:
local pos = obj:getNodePosition(nodeId) - Add mass to a node:
obj:setNodeMass(nodeId, newMass) - Check if a part is broken (via DamageTracker):
local isBroken = damageTracker.getDamage("body", "hood") - Break a group of beams:
beamstate.breakBreakGroup("groupName") - Get distance between two nodes:
local dist = obj:getNodePosition(id1):distance(obj:getNodePosition(id2)) - Apply a force vector to a node:
obj:applyForceVector(nodeId, vec3(0, 5000, 0)) - Get vehicle world rotation (Quat):
local rot = obj:getRotation() - Check if vehicle is upright:
local up = obj:getDirectionVectorUp(); if up.z > 0.8 then ... end - Deflate a specific tire:
beamstate.deflateTire(wheelId) - Get total vehicle mass:
local mass = 0; for _, n in pairs(v.data.nodes) do mass = mass + n.nodeWeight end - Apply a global torque impulse:
obj:applyTorqueAxisCouple(500, node1, node2, node3) - Check for node collision: Hook into
onNodeCollision(p).
3. Input & Control
- Override user steering:
electrics.values.steeringOverride = 0.5 - Disable user throttle:
electrics.values.throttleOverride = 0 - Read raw user steering:
local rawSteer = electrics.values.steering_input - Detect controller button event: Use
input.event("name", val, filter) - Create a non-blocking timer:
local t = HighPerfTimer(); ... if t:stop() > 1000 then ... end - Throttled update (2Hz):
timer = timer + dt; if timer > 0.5 then timer = 0; ... end - Toggle hazard lights:
electrics.toggle_warn_signal() - Set ignition to 'On':
electrics.setIgnitionLevel(2) - Check if parking brake is on:
if (electrics.values.parkingbrake or 0) > 0 then ... end - Smooth a value over time:
val = smoother:get(target, dt)
4. UI & Communication
- Trigger screen message:
guihooks.message("Impact!", 5, "safety") - Send data to UI app:
guihooks.queueStream("myKey", myValue) - Call function in Game Engine:
obj:queueGameEngineLua("print('Hello')") - Send command to another vehicle:
obj:queueObjectLuaCommand(targetId, "electrics.values.horn = 1") - Play a one-time sound effect:
obj:playSFXOnce("event:>Vehicle>Failures>tire_burst", nodeId, 1, 1) - Register damage update callback:
damageTracker.registerDamageUpdateCallback(myFunc) - Check if first player is seated:
if playerInfo.firstPlayerSeated then ... end - Draw debug line in world:
obj.debugDrawProxy:drawLine(pos1, pos2, color) - Draw debug text at node:
obj.debugDrawProxy:drawNodeText(nodeId, color, "Broken!") - Sync variable to peer vehicle:
obj:queueObjectLuaCommand(id, "myModule.syncValue(" .. myVal .. ")")
5. Niche Modding Tasks
- Get vehicle's unique object ID:
local id = obj:getId() - Check if in walking/unicycle mode:
local isWalking = (controller.mainController.typeName == "playerController") - Disable a powertrain device:
powertrain.getDevice("rearDiff"):disable() - Check if a node is underwater:
if obj:inWater(nodeId) then ... end - Get environmental air density:
local density = obj:getRelativeAirDensity() - Check if any player is in vehicle:
if playerInfo.anyPlayerSeated then ... end - Change node friction at runtime:
obj:setNodeFrictionSlidingCoefs(id, 1.0, 0.8) - Get gravity magnitude:
local g = powertrain.currentGravity - Trigger custom UI event:
guihooks.trigger("MyEvent", {val = 1}) - Lock a differential:
powertrain.setDeviceMode("rearDiff", "locked")
See Also
- Damage Overview - Understanding damage systems
- TL;DR Architect - Quick architecture reference
- Lua Patterns - General Lua patterns
- Getting Started - Extension basics
VE Architecture Quick Reference
10-second refresher on Vehicle Engine core rules — lifecycle hooks, override patterns, and the no-ghost-values rule.
Vehicle Modding Guide
How to add custom logic to vehicles safely — extensions vs controllers, the electrics data bus, powertrain API, safe overrides, and UI visibility.