Vehicles#
A vehicle is an object somebody can get into and drive: a car, a bike, a truck, a boat, a plane.
Use it to
- turn an object into something people can get into and drive
- start from a preset like a car, a truck or a bike
- tune the engine, wheels, brakes and steering
- make boats float and planes lift
- drive one from a script, with nobody inside
local self = require("self") self:vehicle({ preset = "car" })That is a whole car. Click it to get in: W / S drive and brake, A / D steer, Space is the handbrake, Shift is the boost, F gets out.
The object can be one box or a model made of parts. Either way it becomes one body that the physics moves, and it is driven without delay: the driver's own game simulates the car while the region checks it.
The presets#
preset | What you get |
|---|---|
"car" | A road car. Front-wheel drive, grips, runs wide rather than spinning. |
"drift" | Rear-wheel drive, a lot of power and steering lock. The handbrake throws the tail out and the throttle holds it there. |
"offroad" | Four-wheel drive, long soft suspension, climbs. |
"truck" | Heavy, slow to turn, strong engine brake. |
"bike" | Two wheels. Leans into the turn by itself and stands back up. |
"boat" | A propeller and a keel. Floats on any water, rides up as it speeds up. |
"plane" | Wings, a tail, an engine and landing gear. Takes off, climbs, banks. |
A preset is a starting point. Every number in it can be replaced, and only the ones you name change:
self:vehicle({ preset = "drift", mass = 1100, engine = { power = 400, top_speed = 240 }, wheels = { rear = { grip = 0.85 } },})self:vehicle(options) → true, or nil, reason
A misspelt option is refused on the line that wrote it, with the names it could have been:
local ok, why = self:vehicle({ engine = { powr = 400 } })-- nil, "unknown option 'engine.powr' (it has: power, top_speed, gears, ...)"self:vehicle(false) stops the object being a vehicle.
Wheels and seats, by name#
When the object is a model made of parts, the vehicle reads the parts' names:
| A part whose name contains | Becomes |
|---|---|
wheel, tire, tyre, roda, pneu | a wheel, where the part is and as big as the part |
front or back / rear in a wheel's name | which end of the vehicle is the front |
seat, banco, assento | a seat |
driver, pilot, motorista, piloto | the driver's seat |
So a downloaded car whose wheels are called wheel-front-left, wheel-back-right and so on needs nothing more than the one line. The wheel parts turn, steer and ride on their suspension by themselves.
With no wheel parts, a car or a bike gets wheels at the corners of its body, which work the same and are simply not drawn. With no seat part, the vehicle itself is the driver's seat.
A seat is an ordinary seat: its place and direction are set in the editor's Physics page, live, while you sit in it. The Driver's seat switch there says which one drives.
Which way is the front#
The front is read from the wheels' names when they say. Otherwise it is the object's forward (-z), or +x when the object is longer along x. Say it when the model points another way:
self:vehicle({ preset = "car", front = "+z" }) -- "-z", "+z", "+x" or "-x"The options#
Positions are in the object's own frame, in metres.
engine#
| Option | Meaning | car |
|---|---|---|
power | peak power, horsepower. 0 = no engine | 150 |
top_speed | km/h it tops out at | 180 |
gears | forward gears; it shifts by itself | 5 |
redline, idle | rev range, for a rev counter or a sound | 6500, 900 |
reverse_speed | km/h in reverse | 35 |
engine_brake | how hard it slows with the throttle closed, m/s² | 1.2 |
boost | power multiplier while Shift is held. 1 = none | 1 |
wheels#
Fields given directly apply to every wheel. front = { … } and rear = { … } apply to one end. A list replaces the wheels altogether.
| Option | Meaning | car |
|---|---|---|
grip | tyre grip. 1.0 is a road tyre on dry road | 1.05 front, 1.25 rear |
slide | share of the grip left once the tyre slides. Lower holds a drift longer | 0.8 |
slip_peak | degrees of slip at peak grip. Higher forgives more | 9 front, 7.5 rear |
travel | suspension travel, metres | 0.2 |
stiffness | ride frequency, Hz. 1.2 is soft, 2.5 is a race car | 1.8 |
damping | 0.3 bounces, 1.0 never overshoots | 0.55 |
steer | share of the steering this wheel takes | 1 front, 0 rear |
drive | share of the engine it receives | 1 front, 0 rear |
brake, handbrake | share of each brake | 1, rear only |
radius | metres. 0 = the part's own size | 0 |
stiffness and damping do not depend on the vehicle's weight: the same numbers ride the same on a kart and on a truck. And the vehicle always sits at the height it was modelled at, whatever it weighs.
self:vehicle({ preset = "car", wheels = { all = { travel = 0.35, stiffness = 1.3 }, rear = { grip = 1.0 } },})More grip behind than in front makes a car that runs wide at the limit and does not spin. Less grip behind makes one that oversteers. That one balance is most of how a car feels.
steering#
| Option | Meaning | car |
|---|---|---|
angle | full lock, degrees | 34 |
speed | how fast the wheels follow the key, degrees per second | 140 |
high_speed | share of the lock left at top speed | 0.22 |
assist | 0..1: how much the lock is held to what the tyres can use | 1 |
drift | degrees of slide the assist holds with the key down | 10 |
drift is the dial between a car that never spins and a car that drifts. With the steering key held, the assist lets the tail hang out this far and then turns the wheels the other way by itself. 10 is a road car; 40 is a drift car. assist = 0 is a raw wheel with no help at all.
brakes#
| Option | Meaning | car |
|---|---|---|
force | foot brake, in g | 1.1 |
handbrake | handbrake, in g, on the wheels that have handbrake | 2.0 |
aero#
| Option | Meaning | car |
|---|---|---|
downforce | extra weight pressing it down at 100 km/h, as a share of its own | 0.15 |
drag | air drag. 0 = worked out from top_speed | 0 |
assist#
| Option | Meaning | car |
|---|---|---|
anti_roll | anti-roll bar. 0 leans like a boat | 0.35 |
low_centre | how far the weight is lowered. Higher is harder to roll over | 0.45 |
traction | 0..1: how much wheelspin is cut | 0 |
balance | 0..1: keeps it upright along its length. A bike needs 1 | 0 |
lean | degrees a balanced vehicle leans into a turn | 0 |
thrusters#
A force at a point: a propeller, a jet, a rocket. A list.
| Option | Meaning |
|---|---|
pos | where it pushes from |
dir | which way it pushes the vehicle. Default is forward |
accel | acceleration it gives the vehicle at full power, m/s² |
top_speed | km/h at which it has nothing left. 0 = no limit |
reverse | share of accel available backwards |
steer | degrees the push swings with the steering: an outboard motor |
axis | "throttle" (W / S), "lever" (a plane's throttle), "boost" (Shift) |
water | true = only pushes under water |
local vec = require("vec") self:vehicle({ preset = "car", thrusters = { { pos = vec(0, 0.5, 2), accel = 14, axis = "boost" } },})wings#
A surface that lifts: lift across the airflow, drag along it. A list.
| Option | Meaning |
|---|---|
pos | where the lift acts |
normal | the side the lift points to: up for a wing, sideways for a fin |
area | m² |
lift | lift per radian of angle of attack |
stall | degrees past which it stops lifting |
incidence | degrees of built-in angle |
control | "pitch", "roll", "yaw" or "none": which control deflects it |
gain | degrees it deflects at full input |
hull#
How the body sits in water, on top of the floating every object already does.
| Option | Meaning | boat |
|---|---|---|
keel | sideways resistance in water. What makes a boat turn instead of skating | 3 |
stability | how hard the water rights it when it heels | 14 |
planing | how much it rides up with speed. A planing hull is what makes a boat fast | 0.45 |
controls#
controls = "car" (the default) or "plane".
| Key | "car" | "plane" |
|---|---|---|
| W / S | drive forward / brake, then reverse | nose down / nose up |
| A / D | steer | roll, with the rudder following |
| Space | handbrake | wheel brakes |
| Shift | boost | throttle up |
| Ctrl | throttle down | |
| F | get out | get out |
Reading it#
vehicle_state#
self:vehicle_state() → a table, or nil when the object is not a vehicle
| Field | Meaning |
|---|---|
speed, kmh | along its front, m/s and km/h. Negative in reverse |
rpm, gear | engine revs, and the gear: 1 up, -1 in reverse |
throttle, steer | what is being asked of it now |
wheels, grounded | how many wheels it has, and how many are on the ground |
slip | 0 gripping .. 1 sliding, the worst wheel |
lever | a plane's throttle lever, 0..1 |
in_water | true while it floats |
driver | the id of whoever is in the driver's seat, or nil |
local self = require("self")local players = require("players") self:vehicle({ preset = "car" }) local DASH = { id = "dash", size = { 150, 60 }, vars = { kmh = 0, gear = 1 }, pages = { main = { { "text", "{kmh} km/h", size = 22, align = "center" }, { "text", "gear {gear}", size = 12, align = "center" }, } },} self:on_sit(function(p) p:show_hud(DASH)end) self:on_stand(function(p) p:hide_hud("dash")end) self:on_timer(0.2, function() local v = self:vehicle_state() local p = v and v.driver and players:get(v.driver) if p then p:update_hud("dash", { kmh = math.floor(math.abs(v.kmh)), gear = v.gear }) endend)vehicle_input#
self:vehicle_input(controls) → true, or nil, reason
The script drives. For a car with nobody in it: traffic, a taxi, a ghost.
| Field | Range |
|---|---|
throttle | -1..1 |
steer | -1..1, right is positive |
brake, handbrake | 0..1 |
boost | true / false |
pitch, roll, yaw | -1..1, for a plane |
lever | 0..1, a plane's throttle |
self:vehicle_input(nil) hands the vehicle back to whoever sits in the driver's seat.
self:vehicle({ preset = "car" })self:vehicle_input({ throttle = 0.5, steer = 0.4 })In multiplayer#
The driver's own machine runs the vehicle they drive. Steering, braking and the camera answer on the frame a key is pressed, at any ping. Everybody else sees the vehicle a moment behind, exactly in step with its driver's avatar.
The server checks every report it gets about a driven vehicle. It refuses a vehicle going faster than its own engine.top_speed (or its fastest thruster) allows, one that jumps from place to place, one that goes through a wall, and one under the ground. A refused vehicle goes back to the last place the server accepted.
- A script that pushes a driven vehicle (
self:push,self:set_velocity) takes it back from the driver for that moment. The push lands exactly as scripted. - A script driving with
self:vehicle_inputdrives it on the server, and the driver's keys wait untilself:vehicle_input(nil). - Getting out of a moving vehicle brakes it to a stop first. Holding the leave key gets out at once, whatever the vehicle is doing.
Loose physics objects work the same way while someone on foot pushes them. The pusher's machine moves the object, and the server keeps it within reach of that player. When a second player walks into the object, the server takes it back.
Common mistakes#
| Symptom | Cause |
|---|---|
| it drives backwards | the model faces the other way. Set front |
| the wheels do not turn | the wheel parts' names do not contain wheel. Rename them, or it uses unseen wheels at the corners |
| it spins every time | less grip behind than in front. Raise wheels.rear.grip, or lower steering.drift |
| it will not drift | steering.drift is small, or the rear grips too well. Start from preset = "drift" |
| it rolls over in corners | raise assist.low_centre and assist.anti_roll |
| the boat does not move | its thruster has water = true and is above the water. Lower its pos |
| the plane will not leave the ground | it needs speed first: hold Shift down the runway, then S |
See also#
- Being sat on: seats, and how a script seats somebody
- Joints: doors, lifts and other things that move on a hinge
- HUD reference: for a dashboard
Updated 17 October 2026