Skip to content

Vehicle asset authoring contracts

Vehicle models are consumed by dealership previews, the customization editor, garage previews, and spawned vehicles. These systems intentionally share the following structural invariants.

This document is the source of truth for cross-runtime model naming and authoring contracts. For the complete assembly-v1 model hierarchy, definition files, and tags, see Vehicle assets. For the meaning and persistence of customization values, see Vehicle customization.

Published vehicle and option identifiers are also saved-data contracts. See Vehicle car data and persistence before renaming or removing definitions used by owned vehicles.

Paint targets

  • Primary body paint uses body parts named Paint. Existing supported assets may also use the Paintable tag or paint:<slot> names.
  • Secondary body paint uses body parts named Secondary.
  • Every part named Paint beneath Wheels is a brake-caliper paint target.
  • Do not place unrelated parts named Paint beneath Wheels.
  • A paint slot without a valid model target is hidden from the customization editor.

These names are asset-domain contracts. Changing them requires updating the shared paint-target implementation and checking every preview and spawn path.

Assembly accessories

  • Give each distinct purchasable accessory a unique part ID. Reuse an ID only when it is intentionally the same item in multiple compatible locations.
  • Accessories included with the vehicle, including nested accessories, are automatically owned by its purchaser.
  • Give required sockets a valid default option.
  • Give each distinct purchasable livery a unique livery ID. Reuse an ID only when the same purchase should be usable in multiple compatible locations.
  • Treat published part and livery IDs as permanent. Ask a programmer before renaming or removing one.

Accessory images

Store accessory images under assets/images/vehicle_accessory/<vehicle>/ using descriptive snake_case filenames. Put an image under assets/images/vehicle_accessory/shared/ when the same artwork is intentionally reused by multiple vehicles; do not keep byte-identical vehicle-specific copies.

Run asphalt sync after adding or replacing images. Asphalt uploads the files and regenerates src-new/shared/assets/images.luau; vehicle definitions should reference that generated module instead of hard-coding Roblox asset IDs.

Require the generated module directly with local images = require(ReplicatedStorage.shared.assets.images), then use the complete generated path at each definition, such as images.vehicle_accessory.shared.roof_bike. Do not create local aliases for vehicle or shared image tables; keeping each reference fully expanded makes definitions straightforward to generate mechanically.

Set each accessory definition's image_width from the source image's aspect ratio:

image_width = 50 * (pixel width / pixel height)

This makes a square image 50 pixels wide, a 2:1 image 100 pixels wide, a 3:1 image 150 pixels wide, and a 4:1 image 200 pixels wide. Preserve the image's aspect ratio and use the corresponding exact width for other ratios following the same linear rule.