# Vehicles

> Drive a toy car on the ground plane with throttle and steering from a joystick, keys or code; use the built-in Car or your own .glb with named wheels; chase it with FollowCamera; read its position and speed.

`<Vehicle>` drives its children like a toy car on the ground plane (y = 0): throttle and steering go in; position, heading, speed and wheel motion come out. The model is arcade on purpose: responsive, no skidding, no tipping over. The scene renders only while the car moves or an input is held.

```tsx title="Drive.tsx"
import { Car, ContactShadow, Ground, Trees } from "@apricot/assets";
import { FollowCamera, Scene, type SceneObject, Vehicle } from "@apricot/react";
import { useRef } from "react";
import { View } from "react-native";
import { Joystick, useJoystickValues } from "./Joystick";

export function Drive() {
  const car = useRef<SceneObject>(null);
  const { throttle, steer } = useJoystickValues();
  return (
    <View style={{ flex: 1 }}>
      <Scene style={{ flex: 1 }} look="soft" fog camera={{ position: [0, 2.6, -6], fov: 55 }}>
        <Ground />
        <Trees count={140} seed={3} />
        <Vehicle ref={car} throttle={throttle} steer={steer} bounds={[-90, -90, 90, 90]}>
          <Car />
          <ContactShadow width={1.5} length={2.5} opacity={0.4} />
        </Vehicle>
        <FollowCamera target={car} />
      </Scene>
      <Joystick throttle={throttle} steer={steer} style={{ position: "absolute", bottom: 40, alignSelf: "center" }} />
    </View>
  );
}
```

`Joystick` and `useJoystickValues` come from the [drive-joystick recipe](https://matthiasbrandolin.dev/apricot/recipes/drive-joystick): copy that file into your app.

## Inputs

| Prop | Range | Meaning |
| --- | --- | --- |
| `throttle` | −1 to 1 | 1 full throttle forwards; negative brakes, then reverses |
| `steer` | −1 to 1 | −1 full left, 1 full right |

Both take a plain number or a **shared value**. Shared values are read every frame without React renders: use them for joysticks, sliders and gestures. Plain numbers suit buttons and key presses (`throttle={gas ? 1 : 0}`).

## The car

Anything inside `<Vehicle>` moves with it. Descendants whose names start with `wheel_f` turn with the steering and spin; those starting with `wheel_r` spin.

- **Built-in:** `<Car color glass>` from `@apricot/assets` is a toy car sized for the defaults (wheel radius 0.3, wheel base 1.4), with wheel nodes already named.
- **Your own `.glb`:** use `<Model src>` inside `<Vehicle>`. Prepare it so that:
  - it faces **+z**, y up, in metres, with the wheels touching y = 0;
  - the wheel nodes are named `wheel_fl`, `wheel_fr`, `wheel_rl`, `wheel_rr`, each with its origin at the wheel's centre (they turn around their own origin: x for spin, y for steering);
  - it has been through `npx apricot-models` (see [Loading models](https://matthiasbrandolin.dev/apricot/guides/models#1-prepare-the-file)).
  
  Then set `wheelRadius` (and `wheelBase`, the distance between the axles) to match it, so the wheels roll without sliding.

```tsx title="MyCar.tsx"
import { Vehicle, type VehicleProps } from "@apricot/react";
import { Model } from "@apricot/react/model";

export const MyCar = (props: VehicleProps) => (
  <Vehicle wheelRadius={0.34} wheelBase={2.7} maxSpeed={20} {...props}>
    <Model src={require("./assets/models/sedan.glb")} />
  </Vehicle>
);
```

## Handling

The options are read **once, at mount**. To change them, remount (change the `key`).

| Option | Default | Meaning |
| --- | --- | --- |
| `maxSpeed` | `12` | Top speed forwards, units per second |
| `maxReverse` | `4` | Top speed in reverse |
| `acceleration` | `9` | At full throttle, units per second² |
| `braking` | `20` | Deceleration with throttle against the motion |
| `drag` | `3` | Deceleration when coasting |
| `wheelBase` | `1.4` | Distance between the axles: shorter turns tighter |
| `maxSteer` | `0.6` | Largest front-wheel angle, radians |
| `steerSpeed` | `8` | How fast the wheels reach the requested angle, per second |
| `wheelRadius` | `0.3` | For the wheels' spin |
| `bounds` | none | `[minX, minZ, maxX, maxZ]`: keeps the car inside this box |
| `start` | `{ x: 0, z: 0, heading: 0 }` | Start place and heading (radians; 0 faces +z) |

## Following it

`<FollowCamera target={ref}>` chases the car from behind and above. Put it **after** the `<Vehicle>` so it follows this frame's position, not the last one. See [Camera](https://matthiasbrandolin.dev/apricot/guides/camera#followcamera-a-chase-camera). `<CameraRig follow={ref}>` also works, for an orbiting view instead of a chase view.

## Reading the car's state

`onUpdate` is called after every simulated frame, on the JS thread, with a `VehicleState`:

| Field | Meaning |
| --- | --- |
| `x`, `z` | Position on the ground |
| `heading` | Radians about +y; 0 faces +z |
| `speed` | Signed speed along the heading, units per second |
| `steer` | Current front-wheel angle, radians (positive steers right) |
| `wheelSpin` | Accumulated wheel rotation, radians |

Do not set React state every frame from it. Keep values in a ref, or throttle updates:

```tsx title="Speedometer.tsx"
import { Car, Ground } from "@apricot/assets";
import { FollowCamera, Scene, type SceneObject, Vehicle, type VehicleState } from "@apricot/react";
import { useRef, useState } from "react";
import { Text, View } from "react-native";

export function Speedometer({ throttle, steer }: { throttle: number; steer: number }) {
  const car = useRef<SceneObject>(null);
  const [kmh, setKmh] = useState(0);
  const onUpdate = (s: Readonly<VehicleState>) => {
    const next = Math.round(Math.abs(s.speed) * 3.6);
    if (next !== kmh) setKmh(next);
  };
  return (
    <View style={{ flex: 1 }}>
      <Scene style={{ flex: 1 }} look="soft">
        <Ground />
        <Vehicle ref={car} throttle={throttle} steer={steer} onUpdate={onUpdate}>
          <Car />
        </Vehicle>
        <FollowCamera target={car} />
      </Scene>
      <Text style={{ position: "absolute", top: 60, alignSelf: "center", fontSize: 32 }}>{kmh} km/h</Text>
    </View>
  );
}
```

## Related

- [drive-joystick recipe](https://matthiasbrandolin.dev/apricot/recipes/drive-joystick).
- [Motion reference](https://matthiasbrandolin.dev/apricot/reference/motion#vehicle).
- [Assets](https://matthiasbrandolin.dev/apricot/reference/assets): `Car`, `Ground`, `Trees`, `ContactShadow`.
