Files
spark/docs/controls.md
T
Diego Marcos 3332f609a9 Initial commit
2025-05-24 00:14:53 -07:00

153 lines
5.8 KiB
Markdown

# Controls
A program using `Forge` can use any camera control scheme they want that is compatible with Three.js and will typically manipulate a `THREE.Camera` object's transform. `Forge` ships with simple, intuitive controls for navigating 3D space that use the keyboard + mouse, game pad, or mobile multi-touch. To add these controls, you can add:
```typescript
const controls = new ForgeControls({
canvas: HTMLCanvasElement;
});
renderer.setAnimationLoop((time) => {
renderer.render(scene, camera);)
controls.update(camera);
});
```
`ForgeControls` instantiates two classes `FpsMovement` and `PointerControls` that it updates internally. You can also instantiate and use these two classes separately:
## `class FpsMovement`
`FpsMovement` implements controls that will be familiar to anyone who plays First Person Shooters using keyboard + mouse or a gamepad. Creating a `FpsMovement` instance provides many parameters:
```typescript
const fpsMovement = new FpsMovement({
moveSpeed?: number;
rollSpeed?: number;
stickThreshold?: number;
rotateSpeed?: number;
keycodeMoveMapping?: { [key: string]: THREE.Vector3 };
keycodeRotateMapping?: { [key: string]: THREE.Vector3 };
gamepadMapping?: {
[button: number]: "shift" | "ctrl" | "rollLeft" | "rollRight";
};
capsMultiplier?: number;
shiftMultiplier?: number;
ctrlMultiplier?: number;
xr?: THREE.WebXRManager;
});
```
When gamepads are connected, `FpsMovement` will always use gamepad index 0 for twin-stick movement and rotation.
If `xr` is passed in, the WebXR controllers can be used as a split gamepad to control movement and rotation. (tested on Quest 3)
### Optional parameters
| Parameter | Default | Description |
|-----------|---------|-------------|
| `moveSpeed` | `1.0` | Base movement speed |
| `rollSpeed` | `2.0` | Speed of roll rotation |
| `stickThreshold` | `0.1` | Deadzone for gamepad analog sticks |
| `rotateSpeed` | `2.0` | Speed of rotation when using gamepad or keys |
| `keycodeMoveMapping` | `{...WASD_KEYCODE_MOVE, ...ARROW_KEYCODE_MOVE}` | Maps keyboard keys to movement directions |
| `keycodeRotateMapping` | `{...QE_KEYCODE_ROTATE, ...ARROW_KEYCODE_ROTATE}` | Maps keyboard keys to rotation directions |
| `gamepadMapping` | `{4: "rollLeft", 5: "rollRight", 6: "ctrl", 7: "shift"}` | Maps gamepad buttons to actions |
| `capsMultiplier` | `10.0` | Speed multiplier when Caps Lock is active |
| `shiftMultiplier` | `5.0` | Speed multiplier when Shift is held |
| `ctrlMultiplier` | `0.2` | Speed multiplier when Ctrl is held |
| `xr` | `undefined` | Optional WebXR manager for XR controller stick support
### `update(deltaTime, control)`
Call this method in your render loop with `control` set to the object to control (`THREE.Camera` or a `THREE.Object3D` that contains it), with `deltaTime` in seconds since the last update.
The update method handles:
- Processing keyboard input for movement and rotation
- Processing gamepad input for movement and rotation
- Applying speed multipliers based on modifier keys and gamepad buttons
- Applying movement and rotation to the controlled object
## `class PointerControls`
`PointerControls` implements pointer/mouse/touch controls on the canvas, for both desktop and mobile web applications. Creating a new control:
```typescript
const pointerControls = new PointerControls({
canvas: HTMLCanvasElement;
rotateSpeed?: number;
slideSpeed?: number;
scrollSpeed?: number;
reverseRotate?: boolean;
reverseSlide?: boolean;
reverseSwipe?: boolean;
reverseScroll?: boolean;
moveInertia?: number;
rotateInertia?: number;
doublePress?: ({
position,
intervalMs,
}: { position: THREE.Vector2; intervalMs: number }) => void;
})
```
### Require parameters
| Parameter | Description |
|-----------|-------------|
| `canvas` | The HTML canvas element to attach pointer events to |
### Optional parameters
| Parameter | Default | Description |
|-----------|---------|-------------|
| `rotateSpeed` | `0.002` | Speed of rotation when dragging |
| `slideSpeed` | `0.006` | Speed of sliding when dragging with right button or two fingers |
| `scrollSpeed` | `0.0015` | Speed of movement when using mouse wheel |
| `reverseRotate` | `false` | Reverse the direction of rotation |
| `reverseSlide` | `false` | Reverse the direction of sliding |
| `reverseSwipe` | `false` | Reverse the direction of swipe gestures |
| `reverseScroll` | `false` | Reverse the direction of scroll wheel movement |
| `moveInertia` | `0.15` | Inertia factor for movement |
| `rotateInertia` | `0.15` | Inertia factor for rotation |
| `doublePress` | `undefined` | Callback function for double-press/double-tap events |
### `update(deltaTime, control)`
Call this method in your render loop with `control` set to the object to control (`THREE.Camera` or a `THREE.Object3D` that contains it), with `deltaTime` in seconds since the last update.
The update method handles:
- Processing pointer/mouse/touch movements for rotation
- Handling dual-press touch gestures for camera movement
- Applying scroll wheel input
- Calculating and applying inertia for smooth motion
- Updating the camera position and orientation
## Adding a simple GUI to configure controls
Add `lil-gui` to your package (`npm add lil-gui`) to provide a simple configurable GUI.
```typescript
import GUI from "lil-gui";
const gui = new GUI({ title: "Settings + Controls" }).close();
const controlOptions = {
reversePointerFps: false,
reversePointerPan: false,
};
gui
.add(controlOptions, "reversePointerFps")
.name("Reverse Pointer FPS")
.onChange((value: boolean) => {
pointerControls.reverseRotate = value;
pointerControls.reverseScroll = value;
});
gui
.add(controlOptions, "reversePointerPan")
.name("Reverse Pointer Pan")
.onChange((value: boolean) => {
pointerControls.reverseSlide = value;
pointerControls.reverseSwipe = value;
});
```