---
title: "Vehicles cars and drivable physics"
description: "The player drives a car, tank, kart, or rover. There is no VehicleComponent and no WheelComponent; a vehicle is composed from the physics atoms. Pick one of two proven paths."
engineVersion: v1.0.234
date: 2026-09-28
license: "(c) Gessa, proprietary. Cite with attribution to https://gessa.ai/docs/. Terms: https://gessa.ai/terms/."
canonical: https://gessa.ai/docs/knowledge/playbooks/vehicles-composed/
---

# Vehicles cars and drivable physics

## When to use

The player drives a car, tank, kart, or rover. There is no VehicleComponent
and no WheelComponent; a vehicle is composed from the physics atoms. Pick one
of two proven paths.

## The recipe

Add components with `project_add_component`; author behavior with
`project_apply_script_semantic_patch` (`ai_safe` pack).

Approach A, joint drivetrain (a true physics vehicle):

1. Chassis: `project_add_component` a dynamic `RigidBodyComponent` plus a solid
   box `ColliderComponent`. Set `RigidBodyComponent.lockRotations`
   `{x:true, y:false, z:true}` so wheel-torque reaction cannot flip it; yaw (y)
   stays free to steer.
2. Wheels: four entities, each a dynamic `RigidBodyComponent`, a solid sphere
   `ColliderComponent` (friction near 1), and a `JointComponent`
   `{jointType:"revolute", connectedEntityId:<chassis>, axis:<lateral>,
   connectedAnchor:<wheel offset in chassis space>, motor:{enabled:true,
   mode:"velocity", targetVelocity:<spin>, damping:<factor>}}`. The runtime builds
   a real Rapier hinge with that motor; spinning wheels plus ground friction drive
   the chassis. Motors exist only on revolute and prismatic joints.
3. Throttle and steer: bind actions (see input-mapping-actions-bindings). In the
   handler (`addActionHandler`, body via `replaceScriptFromGessaScript`) set each
   wheel's `motor.targetVelocity` with `ctx.entity.patchComponent` on the throttle
   action; steer by driving the left and right wheels at different target speeds
   (skid or tank steer).

Approach B, arcade car (script plus physics command; no wheels, robust):

1. One chassis: a dynamic `RigidBodyComponent` plus a solid box
   `ColliderComponent`, same `lockRotations` (free yaw only).
2. Bind throttle and steer actions; in the resolved-action handler
   (`replaceScriptFromGessaScript`, `onActionResolved`) call
   `ctx.physics.command` with `{type:"impulse"}` forward for throttle and
   `{type:"torqueImpulse"}` about the up axis for steering. Name it an arcade car
   honestly: it is force-driven, not a simulated drivetrain.

## Pitfalls

- There is no VehicleComponent, no WheelComponent, no wheel-raycast
  suspension, and no Ackermann steering; compose from `JointComponent` plus
  `RigidBodyComponent` plus `ColliderComponent`.
- A motor or limits on a `fixed`, `spherical`, `spring`, or `rope` joint is
  rejected by the schema (Rapier unit joints); motors live only on `revolute` and
  `prismatic`.
- Unbalanced wheel-motor reaction torque hops or flips a free chassis; lock pitch
  and roll on the chassis so only yaw is free.
- Never patch `VelocityComponent` or `ForceComponent` directly (both hidden and
  runtime-internal); drive motion through `motor.targetVelocity` or
  `ctx.physics.command`.
- Each wheel is its own rigid body and collider, so one four-wheel car is five
  bodies; many cars approach the 1000-collider budget.
- `collideConnected` defaults false, so wheels do not collide with their own
  chassis; leave it false.

## Verify

- `simulation_run` (`qa.run.start`) or the playtest driver: possess the chassis,
  drive throttle then steer, assert the chassis moved and its yaw heading changed,
  and assert no `physics.joint_detached` corrections.
- `project_get_graph_snapshot` to confirm the chassis, the four wheels, and each
  revolute `JointComponent` with an enabled motor.

## What the engine supports (honest note)

The engine supports a true joint-based physics vehicle TODAY, with zero engine
additions: `JointComponent` revolute plus a velocity motor is wired to real Rapier
ImpulseJoints (server/src/modules/runtime/physics/componentSync.ts syncJointState).
Proven in server/tests/vehiclesComposed.test.ts: motorized wheels move a chassis
about 1.86 units forward, an opposed left/right drive yaws it, and the arcade
chassis drives and turns on input through the W2-L playtest driver. What is NOT
present is a dedicated vehicle primitive: no wheel-raycast suspension, no
anti-roll or Ackermann steering, no drivetrain component. Those would be the next
engine primitive; until then a car is the composition above.
