gaugeClusters
A car's speedometer, rev counter and gear display in **ten looks**, switched live with one word. Every style shows the same three things: speed (km/h or mph), revs with a redline zone, and the gear (`R`, `N`, `1`…). Each one is drawn with the engine UI and reads like a real instr…
gaugeClusters
A car's speedometer, rev counter and gear display in ten looks, switched
live with one word. Every style shows the same three things: speed (km/h or
mph), revs with a redline zone, and the gear (R, N, 1…). Each one is
drawn with the engine UI and reads like a real instrument: needles, shift
lights, seven-segment digits, pixel fonts and holographic arcs.
| Style | Looks like |
|---|---|
analog | Classic twin dials in chrome bezels, orange needles, red redline band, gear LCD and shift lamp between them |
digital | Modern glass dashboard: slanted segmented rev bar, big speed, glowing gear tile that flashes SHIFT, pedal bars |
rally | Stage-style: 15 shift lights (green → yellow → red, all blue and blinking at the limit), huge gear, speed, rpm |
retro80s | Neon VFD: rising bar graph for revs, seven-segment speed and gear with unlit segments showing, scanlines |
scifi | Holographic arcs: gradient rev sweep, turning dashed outer ring, speed ring, gear in a hexagon |
minimal | Thin rev line with ticks, big light numbers, nothing else |
arcade | Chunky rainbow speedo blocks with ink outlines, star-burst gear bubble that pops on a shift |
pixel | Pixel-art font and blocks, stepped frame with a hard drop shadow |
hud | Transparent heads-up display, centre low: rev and speed arcs either side of the speed |
truck | Big truck cluster: cream dials, tach, speedo with odometer, boost, fuel and temperature gauges, warning lamps |
First example
Paste this into any world. The dummy car pretends to drive (through the gears, braking, reverse), so the gauge moves with no real car:
local dummy = entity.spawn("dummyCar")
dummy.component.add("FakeCar")
local hud = entity.spawn("hud")
hud.component.add("GaugeCluster", { style = "analog", car = dummy })
-- switch the look live, any time:
hud.component.get("GaugeCluster").style = "rally"
Enter play to see it move. With a real car (for example the torqueCars README car), leave car out: the cluster
follows the local player's own car.
entity.spawn("hud").component.add("GaugeCluster", { style = "digital", units = "mph" })
GaugeCluster settings
| Setting | Default | What it does |
|---|---|---|
style | analog | Which look (the ten above). Switches live. |
units | kmh | kmh or mph. |
car | empty | The car to read. Empty follows the local player's own car: the car announced as player.vehicle on their avatar or the main camera (a torqueCars car does this while the player drives it), else their avatar (or the car it sits in) when that is a car, else the entity the main camera follows. |
anchor | auto | Where it sits: auto (the style's own place), bottomRight, bottomCenter, bottomLeft, topRight, topCenter, topLeft, hud (centre low). |
size | 1 | Extra scale on top of the automatic screen fit. |
maxSpeed | 0 | Top of the speed scale; 0 = the style's own (260 km/h / 160 mph, truck 140 / 90). |
offsetX, offsetY | 0 | Nudge in points. |
visible | true | Draw it. |
Methods: gauge:setStyle(id), gauge:cycleStyle(dir?), gauge:target() (the car
id being read), gauge:reading() (what it shows right now).
From code, without a component
local Gauges = require("gaugeClusters.gauges")
Gauges.show("scifi") -- follows the local player's car
Gauges.show("truck", someCar, { units = "mph", anchor = "bottomLeft" })
Gauges.setStyle("pixel")
Gauges.setCar(otherCar)
Gauges.hide()
Gauges.styles() --> { "analog", "digital", ..., "truck" }
The parts are public too: gaugeClusters.reader reads any car into one
table (read, resolve, gearLabel), gaugeClusters.styles builds each
look as a widget tree (S.get(id).build(view, scale, state)), and
gaugeClusters.draw holds the canvas helpers (arcs, needles, seven-segment
digits, a 3x5 pixel font).
What it reads
The driving contract's named values on the car's chassis entity (see
torqueCars.package/CONTRACT.md): car.kmh (or car.speed), car.rpm,
car.gear (-1 R, 0 N, 1..n), car.rpmRedline, car.rpmMax, and for
flavour car.throttle, car.brake, car.boost, car.limiter. Any car that
writes them works. A plain builtin VehicleController car with no values
reads too: speed from Physics.vehicleState, 0 rpm, gear -.
Gallery
gallery (a scene) shows all ten side by side, driven by a pretend car:
layers.load(asset.ref("gaugeClusters.gallery", "scene"), { additive = true })
Or put GaugeGallery on any entity (focus = "truck" shows one style large).
Screens and phones
Each cluster fits itself to the screen: about a third of a desktop screen's width, nearly the full width of a phone held upright, and never more than about 36% of the height, so the road stays visible. It lays out inside the safe area, so notches, cutouts and gesture bars never cover it. The UI is a personal overlay: each player sees their own car's cluster, and nothing is synced.
What is inside
| Part | What it is |
|---|---|
GaugeCluster (component) | One cluster on screen, owned by the component. |
GaugeGallery (component) | All ten styles in a grid with a pretend car. |
FakeCar (component) | Makes its entity write fake contract values (for tests and demos). |
gauges (module) | show / hide / setStyle API and the per-frame pipeline. |
styles (module) | The ten looks. |
reader (module) | Reads a car (contract values or VehicleController) and finds the local player's car. |
draw (module) | Canvas paint helpers. |
fake (module) | The pretend car's simulation. |
gallery (scene) | The gallery, ready to load. |
Known limits
- What it needs from a car — a car that does not write the driving contract's values shows
speed only (from
Physics.vehicleState), 0 rpm and gear-. - Finding your own car — with
carempty the cluster reads the car announced asplayer.vehicleon the local player's avatar or the main camera (torqueCars cars announce themselves while the player drives them), else the avatar or what the camera follows when that is a car. A car that writes the values but announces nothing needscarset. - #21241 —
caris an entity reference and is not synced; the cluster is a personal overlay, so every player's cluster resolves its own car locally. - Cost — each cluster redraws its UI every frame, about 0.5 ms on a desktop.
- #20946 — phone layouts were checked at phone sizes in the native engine; the web build has not been run.
Scoped to this part · feeds back into the world's score.