### Quick Start: Physics World Setup
Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md
A minimal example demonstrating how to create a physics world with a static ground and dynamic boxes.
```typescript
import { createWorld, WorldSettings, Body, Shape, Material, RigidBody, World } from "crashcat"
const world = createWorld({
gravity: [-9.81, 0, 0],
worldSettings: WorldSettings.default
})
// Ground
world.addBody(new RigidBody({
isStatic: true,
shape: new Shape.Box({ halfExtents: [100, 0.1, 100] }),
}))
// Dynamic boxes
for (let i = 0; i < 10; i++) {
world.addBody(new RigidBody({
position: [Math.random() * 10 - 5, 10 + i * 0.5, Math.random() * 10 - 5],
shape: new Shape.Box({ halfExtents: [0.5, 0.5, 0.5] }),
material: new Material({ restitution: 0.3, friction: 0.7 }),
}))
}
```
--------------------------------
### Initialize and Display Examples
Source: https://github.com/isaac-mason/crashcat/blob/main/examples/index.html
This function populates the sidebar with example items, including titles and tags, fetched from a JSON file. It's used to set up the initial view of the examples list.
```javascript
import examples from "./src/examples.json" assert { type: "json" };
function addExamplesToSidebar() {
const examplesList = document.getElementById("examples-list");
const examplesHTML = Object.entries(examples)
.map(([key, example]) => {
const tagsHTML = example.tags
.map((tag) => `${tag}`)
.join("");
return `
${example.title}
`;
})
.join("");
examplesList.innerHTML = examplesHTML;
}
// initialize examples list
addExamplesToSidebar();
```
--------------------------------
### Quick Start: Create Physics World and Simulate
Source: https://github.com/isaac-mason/crashcat/blob/main/README.md
This snippet demonstrates the basic setup for a Crashcat physics world. It includes registering shapes, configuring world settings, creating static and dynamic rigid bodies, and simulating the world over time. Ensure all necessary modules are imported and registered before use.
```typescript
import {
addBroadphaseLayer,
addObjectLayer,
box,
type CollideShapeHit,
type ContactManifold,
type ContactSettings,
ContactValidateResult,
createWorld,
createWorldSettings,
enableCollision,
type Listener,
MotionType,
type RigidBody,
registerAll,
rigidBody,
updateWorld,
} from 'crashcat';
import type { Vec3 } from 'mathcat';
// register all shapes
registerAll();
// create a simple world
const worldSettings = createWorldSettings();
export const BROADPHASE_LAYER_MOVING = addBroadphaseLayer(worldSettings);
export const BROADPHASE_LAYER_NOT_MOVING = addBroadphaseLayer(worldSettings);
export const OBJECT_LAYER_MOVING = addObjectLayer(worldSettings, BROADPHASE_LAYER_MOVING);
export const OBJECT_LAYER_NOT_MOVING = addObjectLayer(worldSettings, BROADPHASE_LAYER_NOT_MOVING);
enableCollision(worldSettings, OBJECT_LAYER_MOVING, OBJECT_LAYER_NOT_MOVING);
enableCollision(worldSettings, OBJECT_LAYER_MOVING, OBJECT_LAYER_MOVING);
worldSettings.gravity = [0, -9.81, 0];
const world = createWorld(worldSettings);
// create a static ground
rigidBody.create(world, {
motionType: MotionType.STATIC,
shape: box.create({ halfExtents: [10, 1, 10] }),
objectLayer: OBJECT_LAYER_NOT_MOVING,
});
// create a stack of dynamic boxes
for (let i = 0; i < 5; i++) {
rigidBody.create(world, {
motionType: MotionType.DYNAMIC,
shape: box.create({ halfExtents: [1, 1, 1] }),
objectLayer: OBJECT_LAYER_MOVING,
position: [0, 2 + i * 2, 0],
});
}
// simulate 10 seconds
for (let i = 0; i < 60 * 10; i++) {
// typically you will do this in a loop, e.g. requestAnimationFrame or setInterval
// pass 'undefined' for no physics listener
updateWorld(world, undefined, 1 / 60);
}
```
--------------------------------
### Kinematic Character Controller (KCC) Example
Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md
Example demonstrating the setup of a Kinematic Character Controller for precise player movement, including features like wall sliding and stair stepping.
```typescript
const kcc = new KCC();
world.add(kcc);
// Example movement logic
input.onMove((dx, dy) => {
kcc.move(dx, dy, 0);
});
```
--------------------------------
### Implement Example Search Functionality
Source: https://github.com/isaac-mason/crashcat/blob/main/examples/index.html
Filters the displayed examples based on user input in the search bar. It searches through example titles and tags, showing only matching items.
```javascript
const searchInput = document.getElementById("search-input");
// search functionality
function filterExamples() {
const searchTerm = searchInput.value.toLowerCase();
const exampleItems = document.querySelectorAll(".examples-list li");
exampleItems.forEach((item) => {
const exampleKey = item
.querySelector(".example-item")
.getAttribute("data-example");
const example = examples[exampleKey]; // search in title and tags
const searchableText = [
example.title,
...example.tags,
]
.join(" ")
.toLowerCase();
if (searchableText.includes(searchTerm)) {
item.style.display = "";
} else {
item.style.display = "none";
}
});
}
// attach search event listener
searchInput.addEventListener("input", filterExamples);
```
--------------------------------
### Load and Display Selected Example
Source: https://github.com/isaac-mason/crashcat/blob/main/examples/index.html
Loads a specific example into the main frame based on its key. It updates the title, frame source, active item in the sidebar, and the URL hash. Also updates the source link.
```javascript
const titleElement = document.getElementById("example-title");
const frameElement = document.getElementById("example-frame");
function loadExample(exampleKey, scrollIntoView) {
const example = examples[exampleKey];
if (!example) return;
titleElement.textContent = example.title;
frameElement.src = `./${exampleKey}.html`;
// update active item
const exampleItems = document.querySelectorAll(".example-item");
let activeItem = null;
exampleItems.forEach((item) => {
item.classList.remove("active");
if (item.getAttribute("data-example") === exampleKey) {
item.classList.add("active");
activeItem = item;
}
});
// scroll active item into view
if (scrollIntoView && activeItem) {
activeItem.scrollIntoView({ block: "center", behavior: "auto" });
}
// update URL
window.location.hash = exampleKey;
// update source link
const sourceLink = document.getElementById("source-link");
sourceLink.href = `https://github.com/isaac-mason/crashcat/blob/main/examples/src/${exampleKey}.ts`;
// close mobile sidebar
sidebar.classList.remove("open");
sidebarOverlay.classList.remove("show");
}
```
--------------------------------
### Install Crashcat
Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md
Install the crashcat package using npm.
```bash
> npm install crashcat
```
--------------------------------
### Handle URL Hash Changes for Examples
Source: https://github.com/isaac-mason/crashcat/blob/main/examples/index.html
Listens for changes in the URL hash to load the corresponding example. If no hash is present or valid, it loads the first example in the list. This ensures deep linking and correct initial state.
```javascript
// handle hash changes
function handleHashChange(scrollIntoView = false) {
const hash = window.location.hash.substring(1);
if (hash && examples[hash]) {
loadExample(hash, scrollIntoView);
} else {
// load first example by default
const firstExample = Object.keys(examples)[0];
loadExample(firstExample, scrollIntoView);
}
}
// initialize
handleHashChange(true);
window.addEventListener("hashchange", () => handleHashChange(true));
```
--------------------------------
### Handle Sidebar and Example Clicks
Source: https://github.com/isaac-mason/crashcat/blob/main/examples/index.html
Manages user interactions with the sidebar and example items. Clicking an example loads it into the main frame and updates the URL hash. Handles mobile sidebar toggling.
```javascript
const hamburger = document.getElementById("hamburger");
const sidebar = document.getElementById("sidebar");
const sidebarOverlay = document.getElementById("sidebar-overlay");
// handle sidebar clicks
function attachEventListeners() {
const exampleItems = document.querySelectorAll(".example-item");
exampleItems.forEach((item) => {
item.addEventListener("click", (e) => {
e.preventDefault();
const exampleKey = item.getAttribute("data-example");
loadExample(exampleKey);
});
});
}
// mobile navigation
hamburger.addEventListener("click", () => {
sidebar.classList.toggle("open");
sidebarOverlay.classList.toggle("show");
});
sidebarOverlay.addEventListener("click", () => {
sidebar.classList.remove("open");
sidebarOverlay.classList.remove("show");
});
attachEventListeners();
```
--------------------------------
### Crashcat Constraint Types Examples
Source: https://github.com/isaac-mason/crashcat/blob/main/README.md
Provides examples for creating various constraint types in Crashcat, including Point, Distance, Hinge, Slider, and Fixed constraints. Note the specific parameters required for each type.
```typescript
// point constraint - connects two bodies at a point (removes 3 DOF)
const point = pointConstraint.create(world, {
bodyIdA: bodyA.id,
bodyIdB: bodyB.id,
pointA: [0, 1, 0],
pointB: [0, 0, 0],
});
// distance constraint - maintains distance between two points (removes 1 DOF)
const distance = distanceConstraint.create(world, {
bodyIdA: bodyA.id,
bodyIdB: bodyB.id,
pointA: [0, 1, 0],
pointB: [0, 0, 0],
minDistance: 1,
maxDistance: 2,
});
// hinge constraint - allows rotation around an axis (removes 5 DOF)
const hinge = hingeConstraint.create(world, {
bodyIdA: bodyA.id,
bodyIdB: bodyB.id,
pointA: [0, 0.5, 0],
pointB: [0, -0.5, 0],
hingeAxisA: [0, 0, 1],
hingeAxisB: [0, 0, 1],
normalAxisA: [1, 0, 0],
normalAxisB: [1, 0, 0],
space: ConstraintSpace.LOCAL,
});
// slider constraint - allows movement along an axis (removes 5 DOF)
const slider = sliderConstraint.create(world, {
bodyIdA: bodyA.id,
bodyIdB: bodyB.id,
pointA: [0, 0, 0],
pointB: [0, 0, 0],
sliderAxisA: [1, 0, 0],
sliderAxisB: [1, 0, 0],
normalAxisA: [0, 1, 0],
normalAxisB: [0, 1, 0],
space: ConstraintSpace.LOCAL,
limitsMin: -2,
limitsMax: 2,
});
// fixed constraint - completely locks two bodies together (removes 6 DOF)
const fixed = fixedConstraint.create(world, {
bodyIdA: bodyA.id,
bodyIdB: bodyB.id,
point1: [0, 0, 0],
point2: [0, 0, 0],
axisX1: [1, 0, 0],
axisY1: [0, 1, 0],
axisX2: [1, 0, 0],
axisY2: [0, 1, 0],
space: ConstraintSpace.LOCAL,
});
```
--------------------------------
### Configure and Use Query Filters
Source: https://github.com/isaac-mason/crashcat/blob/main/README.md
Demonstrates how to create, modify, and use query filters for various collision scenarios. Includes examples for enabling/disabling layers, setting collision groups/masks, and implementing custom body filter callbacks.
```typescript
// filters control what queries can hit, using object layers and collision groups/masks
// basic: create a filter with all layers enabled
const worldQueryFilter = filter.create(world.settings.layers);
// filter specific object layers
filter.disableObjectLayer(worldQueryFilter, world.settings.layers, OBJECT_LAYER_DEBRIS);
filter.enableObjectLayer(worldQueryFilter, world.settings.layers, OBJECT_LAYER_MOVING);
// filter specific broadphase layers
filter.disableBroadphaseLayer(worldQueryFilter, world.settings.layers, BROADPHASE_LAYER_MOVING);
filter.enableBroadphaseLayer(worldQueryFilter, world.settings.layers, BROADPHASE_LAYER_NOT_MOVING);
// filter everything, then selectively enable
filter.disableAllLayers(worldQueryFilter, world.settings.layers);
filter.enableObjectLayer(worldQueryFilter, world.settings.layers, OBJECT_LAYER_MOVING);
// collision groups and masks (works alongside layer filtering)
worldQueryFilter.collisionGroups = 0b0001; // query belongs to group 1
worldQueryFilter.collisionMask = 0b0010 | 0b0100; // query hits groups 2 and 4
// custom body filter callback
worldQueryFilter.bodyFilter = (body: RigidBody) => {
// custom logic - exclude specific bodies
if (body.userData === 'player') return false;
// only hit dynamic bodies
if (body.motionType !== MotionType.DYNAMIC) return false;
return true;
};
// setFromBody: configure filter to match what a body can collide with
const playerBody = rigidBody.get(world, playerId)!;
filter.setFromBody(worldQueryFilter, world.settings.layers, playerBody);
// copy filter settings
const rayFilter = filter.create(world.settings.layers);
filter.copy(rayFilter, worldQueryFilter);
// use filter in queries
castRay(world, closestCollector, raySettings, rayOrigin, rayDirection, rayLength, worldQueryFilter);
```
--------------------------------
### World Settings Configuration
Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md
Example of configuring world settings, including broadphase and object layers for collision filtering.
```typescript
import { createWorld, WorldSettings, BroadphaseLayer, ObjectLayer } from "crashcat"
const world = createWorld({
gravity: [0, -9.81, 0],
worldSettings: {
...WorldSettings.default,
broadphaseLayers: [
new BroadphaseLayer({ name: "moving", objectLayers: ["player", "enemy", "projectile"] }),
new BroadphaseLayer({ name: "static", objectLayers: ["terrain"] }),
],
objectLayers: [
new ObjectLayer({ name: "player", broadphaseLayer: "moving" }),
new ObjectLayer({ name: "enemy", broadphaseLayer: "moving" }),
new ObjectLayer({ name: "terrain", broadphaseLayer: "static" }),
new ObjectLayer({ name: "projectile", broadphaseLayer: "moving" }),
],
collisionRules: [
{ from: "player", to: "enemy", canCollide: true },
{ from: "player", to: "terrain", canCollide: true },
{ from: "projectile", to: "enemy", canCollide: true },
{ from: "projectile", to: "player", canCollide: false },
],
},
})
```
--------------------------------
### Create and Configure Physics World
Source: https://github.com/isaac-mason/crashcat/blob/main/README.md
Sets up broadphase and object layers, defines collision rules, and creates the physics world with specified settings like gravity. Use registerAll() for easy setup or granular registration for better tree shaking.
```typescript
import { addBroadphaseLayer, addObjectLayer, createWorld, createWorldSettings, enableCollision, registerAll } from 'crashcat';
// we can use registerShapes and registerConstraints to granularly declare
// which shapes and constraints we want to use for the best tree shaking.
// but early in development, it's easier to just register everything.
registerAll();
// this is a container for all settings related to world simulation.
// in a real project, you'd put this in a e.g. "physics-world-settings.ts" file seperate from the physics world
// creation, and import it and below constants where needed.
const worldSettings = createWorldSettings();
// earth gravity
worldSettings.gravity = [0, -9.81, 0];
// we're first up going to define "broadphase layers".
// for simple projects, a "moving" and "not moving" broadphase layer is a good start.
export const BROADPHASE_LAYER_MOVING = addBroadphaseLayer(worldSettings);
export const BROADPHASE_LAYER_NOT_MOVING = addBroadphaseLayer(worldSettings);
// next, we'll define some "object layers".
export const OBJECT_LAYER_MOVING = addObjectLayer(worldSettings, BROADPHASE_LAYER_MOVING);
export const OBJECT_LAYER_NOT_MOVING = addObjectLayer(worldSettings, BROADPHASE_LAYER_NOT_MOVING);
// here we declare that "moving" objects should collide with "not moving" objects, and with other "moving" objects.
enableCollision(worldSettings, OBJECT_LAYER_MOVING, OBJECT_LAYER_NOT_MOVING);
enableCollision(worldSettings, OBJECT_LAYER_MOVING, OBJECT_LAYER_MOVING);
// time to create the physics world from the settings we've defined
const world = createWorld(worldSettings);
```
--------------------------------
### Floating Capsule Character Controller Example
Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md
User-land implementation of a dynamic character controller using a rigid body and constraints. Suitable for AI or physics-based characters.
```typescript
const body = new RigidBody({ /* ... */ });
const capsuleShape = new CapsuleShape(radius, height);
world.add(body, capsuleShape);
// Add constraints to keep the capsule upright
const rotationConstraint = new RotationConstraint();
body.addConstraint(rotationConstraint);
```
--------------------------------
### Three.js Debug Renderer Example
Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md
This snippet demonstrates how to use the Three.js debug renderer provided by the 'crashcat/three' package to visualize physics simulation state during development. It's useful for debugging but can impact performance if visualizing many elements.
```typescript
import * as THREE from 'three';
import { World } from 'crashcat';
import { DebugRenderer } from 'crashcat/three';
const world = new World();
const scene = new THREE.Scene();
const renderer = new THREE.WebGLRenderer();
// ... setup world and scene ...
const debugRenderer = new DebugRenderer(world, scene);
function animate() {
world.updateWorld(1 / 60);
debugRenderer.render();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
```
--------------------------------
### Change Rigid Body Motion Type
Source: https://github.com/isaac-mason/crashcat/blob/main/README.md
Shows how to change a rigid body's motion type dynamically at runtime. This allows switching between static, kinematic, and dynamic behaviors, for example, when a player picks up or drops an object.
```typescript
// change a body's motion type at runtime
const switchableBody = rigidBody.create(world, {
shape: box.create({ halfExtents: [1, 1, 1] }),
motionType: MotionType.DYNAMIC,
objectLayer: OBJECT_LAYER_MOVING,
});
// make it kinematic (e.g. player grabs it)
rigidBody.setMotionType(world, switchableBody, MotionType.KINEMATIC, true);
// make it dynamic again (e.g. player drops it)
rigidBody.setMotionType(world, switchableBody, MotionType.DYNAMIC, true);
// make it static (e.g. permanently attach to world)
rigidBody.setMotionType(world, switchableBody, MotionType.STATIC, false);
```
--------------------------------
### Selective Shape Registration
Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md
Register only the shapes you need to reduce bundle size. This example includes sphere, box, and capsule shapes.
```typescript
import { registerShapes } from "crashcat";
registerShapes({
sphere: sphere.create,
box: box.create,
capsule: capsule.create,
});
```
--------------------------------
### Create Rigid Bodies with Different Motion Types
Source: https://github.com/isaac-mason/crashcat/blob/main/README.md
Shows how to create static, dynamic, and kinematic rigid bodies. Static bodies are immovable, dynamic bodies are fully simulated, and kinematic bodies are controlled by user velocity and can push dynamic bodies.
```typescript
// static: cannot move, infinite mass, not affected by forces
const staticBody = rigidBody.create(world, {
shape: box.create({ halfExtents: [5, 0.5, 5] }),
motionType: MotionType.STATIC,
objectLayer: OBJECT_LAYER_NOT_MOVING,
});
// dynamic: fully simulated, affected by forces, gravity, and contacts
const dynamicBody = rigidBody.create(world, {
shape: sphere.create({ radius: 1 }),
motionType: MotionType.DYNAMIC,
objectLayer: OBJECT_LAYER_MOVING,
});
// kinematic: user-controlled velocity, pushes dynamic bodies
const kinematicBody = rigidBody.create(world, {
shape: box.create({ halfExtents: [2, 0.2, 2] }),
motionType: MotionType.KINEMATIC,
objectLayer: OBJECT_LAYER_MOVING,
});
```
--------------------------------
### Create and Remove a Point Constraint
Source: https://github.com/isaac-mason/crashcat/blob/main/README.md
Demonstrates the creation of two rigid bodies and a point constraint connecting them. Shows how to enable/disable and remove the constraint.
```typescript
const world = createWorld(createWorldSettings());
// create two bodies
const bodyA = rigidBody.create(world, {
shape: box.create({ halfExtents: [0.5, 0.5, 0.5] }),
objectLayer: 0,
motionType: MotionType.STATIC,
position: [0, 5, 0],
});
const bodyB = rigidBody.create(world, {
shape: sphere.create({ radius: 0.5 }),
objectLayer: 1,
motionType: MotionType.DYNAMIC,
position: [0, 3, 0],
});
// create a point constraint connecting them
const constraint = pointConstraint.create(world, {
bodyIdA: bodyA.id,
bodyIdB: bodyB.id,
pointA: [0, 4.5, 0], // point on bodyA in world space
pointB: [0, 3.5, 0], // point on bodyB in world space
space: ConstraintSpace.WORLD,
});
// constraints can be enabled/disabled
constraint.enabled = false;
// remove constraint when done
pointConstraint.remove(world, constraint);
```
--------------------------------
### Physics and Rendering Units
Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md
Crashcat uses SI units (meters, kilograms, seconds). Scale physics positions to rendering pixels for display. This example shows a human-scale body and rendering at 50 pixels per meter.
```typescript
// physics: 1 meter = human-scale
const body = rigidBody.create(world, {
shape: capsule.create({ radius: 0.3, halfHeightOfCylinder: 0.7 }), // ~1.4m tall
position: [0, 10, 0], // 10 meters up
// ...
});
// rendering: scale physics position to pixels
const renderPosition = [
body.position[0] * 50, // 50 pixels per meter
body.position[1] * 50,
body.position[2] * 50,
];
```
--------------------------------
### Register All Shapes and Constraints
Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md
Use registerAll() for simplicity during development. This must be called before creating any bodies or constraints.
```typescript
import { registerAll } from "crashcat";
// MUST call this before updateWorld, or doing world queries
registerAll();
```
--------------------------------
### Create Rigid Body with Automatic Mass Properties
Source: https://github.com/isaac-mason/crashcat/blob/main/README.md
Shows how to create a dynamic rigid body where mass properties are automatically calculated based on shape and density.
```typescript
// mass properties are computed automatically from shape and density
const body = rigidBody.create(world, {
shape: sphere.create({ radius: 1, density: 1000 }), // density in kg/m³
motionType: MotionType.DYNAMIC,
objectLayer: OBJECT_LAYER_MOVING,
});
```
--------------------------------
### Create and Remove Rigid Bodies
Source: https://github.com/isaac-mason/crashcat/blob/main/README.md
Demonstrates how to create dynamic and static box bodies, and how to remove a body from the simulation world. Ensure correct object layers are assigned for collision filtering.
```typescript
const dynamicBox = rigidBody.create(world, {
shape: box.create({ halfExtents: [1, 1, 1] }),
motionType: MotionType.DYNAMIC,
objectLayer: OBJECT_LAYER_MOVING,
position: [0, 5, 0],
});
const ground = rigidBody.create(world, {
shape: box.create({ halfExtents: [10, 0.5, 10] }),
motionType: MotionType.STATIC,
objectLayer: OBJECT_LAYER_NOT_MOVING,
position: [0, -0.5, 0],
});
rigidBody.remove(world, dynamicBox);
```
--------------------------------
### Basic Constraint Creation
Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md
Demonstrates the creation and removal of a basic constraint between two bodies.
```typescript
const bodyA = world.createBody({ position: new Vector2(0, 0) });
const bodyB = world.createBody({ position: new Vector2(10, 0) });
const constraint = new PointConstraint(bodyA, bodyB);
world.createConstraint(constraint);
// ... later ...
world.destroyConstraint(constraint);
```
--------------------------------
### Create Rigid Bodies with Material Properties
Source: https://github.com/isaac-mason/crashcat/blob/main/README.md
Demonstrates creating rigid bodies with different friction and restitution values. Use these settings to control how surfaces interact during collisions. Custom combine modes can also be specified.
```typescript
// low friction, high restitution
const bouncyBall = rigidBody.create(world, {
shape: sphere.create({ radius: 1 }),
motionType: MotionType.DYNAMIC,
objectLayer: OBJECT_LAYER_MOVING,
friction: 0.1,
restitution: 0.9,
});
// high friction, no bounce
const stickyBox = rigidBody.create(world, {
shape: box.create({ halfExtents: [1, 1, 1] }),
motionType: MotionType.DYNAMIC,
objectLayer: OBJECT_LAYER_MOVING,
friction: 1.0,
restitution: 0.0,
});
// custom combine modes
const customCombine = rigidBody.create(world, {
shape: sphere.create({ radius: 1 }),
motionType: MotionType.DYNAMIC,
objectLayer: OBJECT_LAYER_MOVING,
friction: 0.5,
restitution: 0.5,
frictionCombineMode: MaterialCombineMode.MIN,
restitutionCombineMode: MaterialCombineMode.MAX,
});
```
--------------------------------
### Read and Set Rigid Body Transform
Source: https://github.com/isaac-mason/crashcat/blob/main/README.md
Demonstrates how to read the current position and quaternion of a rigid body, and how to set them together or separately.
```typescript
// read position and quaternion
const position = dynamicBody.position; // [x, y, z]
const quaternion = dynamicBody.quaternion; // [x, y, z, w]
// set position and quaternion together
const newPosition = vec3.fromValues(0, 10, 0);
const newQuaternion = quat.create();
rigidBody.setTransform(world, kinematicBody, newPosition, newQuaternion, false);
// set position and quaternion separately
igidBody.setPosition(world, kinematicBody, newPosition, false);
igidBody.setQuaternion(world, kinematicBody, newQuaternion, false);
```
--------------------------------
### Apply Forces and Impulses to Rigid Bodies
Source: https://github.com/isaac-mason/crashcat/blob/main/README.md
Illustrates how to add forces and impulses to a rigid body, both at its center of mass and at specific world positions to induce rotation.
```typescript
// add force at center of mass (accumulates until next physics step)
const force = vec3.fromValues(0, 100, 0); // upward force
rigidBody.addForce(world, dynamicBody, force, true); // last arg: wake if sleeping
// add force at specific position (generates torque)
const worldPosition = vec3.fromValues(1, 0, 0); // apply force at right edge
rigidBody.addForceAtPosition(world, dynamicBody, force, worldPosition, true);
// add impulse (instant velocity change at center of mass)
const impulse = vec3.fromValues(0, 10, 0);
rigidBody.addImpulse(world, dynamicBody, impulse);
// add impulse at position (instant velocity + angular velocity change)
rigidBody.addImpulseAtPosition(world, dynamicBody, impulse, worldPosition);
```
--------------------------------
### Create and Remove Rigid Bodies
Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md
Demonstrates the creation and removal of rigid bodies. Be cautious about storing direct references to body objects due to internal pooling; prefer storing and retrieving by ID.
```typescript
const body = rigidBody.create(
{
position: new Vector3(0, 10, 0),
rotation: new Quaternion(0, 0, 0, 1),
// ... other properties
},
{
// ... other options
}
);
// ... later
rigidBody.destroy(body);
```
--------------------------------
### Fixed Timestep Simulation Update
Source: https://github.com/isaac-mason/crashcat/blob/main/README.md
Steps the physics simulation at a fixed rate (e.g., 60 Hz) for maximum stability and determinism, decoupling it from the render frame rate. Interpolate body positions using alpha for smooth rendering.
```javascript
const PHYSICS_DT = 1 / 60;
let accumulator = 0;
let lastTimeFixed = performance.now();
function gameLoopFixedTimestep() {
const currentTime = performance.now();
const frameTime = Math.min((currentTime - lastTimeFixed) / 1000, 0.25);
lastTimeFixed = currentTime;
accumulator += frameTime;
// step physics at fixed rate
while (accumulator >= PHYSICS_DT) {
updateWorld(world, undefined, PHYSICS_DT);
accumulator -= PHYSICS_DT;
}
// ... render with interpolation ...
// const alpha = accumulator / PHYSICS_DT;
// interpolate body positions using alpha for smooth rendering
requestAnimationFrame(gameLoopFixedTimestep);
}
```
--------------------------------
### Set Rigid Body Mass Properties
Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md
Demonstrates setting mass properties for rigid bodies. This is usually computed automatically, but must be provided explicitly for triangle meshes.
```typescript
rigidBody.setMassProperties(body, {
mass: 10,
centerOfMass: new Vector3(0, 0, 0),
// ... other properties
});
```
--------------------------------
### Enable Continuous Collision Detection (CCD)
Source: https://github.com/isaac-mason/crashcat/blob/main/README.md
Configure `MotionQuality.LINEAR_CAST` for dynamic bodies to prevent tunneling through thin objects. Adjust the `ccd.linearCastThreshold` in world settings.
```typescript
const bullet = rigidBody.create(world, {
shape: sphere.create({ radius: 0.1 }),
motionType: MotionType.DYNAMIC,
objectLayer: OBJECT_LAYER_MOVING,
motionQuality: MotionQuality.LINEAR_CAST, // enables ccd
});
// configure threshold in world settings (default 0.05 = 5%)
// worldSettings.ccd.linearCastThreshold = 0.05;
```
--------------------------------
### Basic Listener Implementation
Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md
Implement a listener to react to and modify physics events during world updates. Pass the listener to `updateWorld()` to receive collision callbacks.
```typescript
class MyListener implements PhysicsEventListener {
onCollisionEnter(handleA: RigidBodyHandle, handleB: RigidBodyHandle, contactManifold: ContactManifold) {
console.log('Collision detected between', handleA, 'and', handleB);
}
}
const listener = new MyListener();
world.updateWorld(deltaTime, listener);
```
--------------------------------
### Create Rigid Body with Overridden Mass
Source: https://github.com/isaac-mason/crashcat/blob/main/README.md
Demonstrates creating a dynamic rigid body and overriding only its mass, allowing inertia to be scaled automatically.
```typescript
// override just the mass (inertia is scaled automatically)
const customMassBody = rigidBody.create(world, {
shape: box.create({ halfExtents: [1, 1, 1] }),
motionType: MotionType.DYNAMIC,
objectLayer: OBJECT_LAYER_MOVING,
mass: 50, // kg
});
```
--------------------------------
### Create a Sensor Body for Collision Detection
Source: https://github.com/isaac-mason/crashcat/blob/main/README.md
Shows how to create a sensor body that detects collisions without applying physical forces. This is useful for creating trigger zones or detection areas. A listener can be set up to react to bodies entering the sensor.
```typescript
const triggerZone = rigidBody.create(world, {
shape: box.create({ halfExtents: [5, 5, 5] }),
motionType: MotionType.STATIC,
objectLayer: OBJECT_LAYER_NOT_MOVING,
sensor: true,
});
// detect when bodies enter/exit sensor
const listener: Listener = {
onContactAdded: (bodyA, bodyB, manifold, settings) => {
if (bodyA.id === triggerZone.id || bodyB.id === triggerZone.id) {
// otherBody entered trigger zone
const otherBody = bodyA.id === triggerZone.id ? bodyB : bodyA;
}
},
};
```
--------------------------------
### Stepping Simulation: Fixed Timestep
Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md
Implement a fixed physics timestep with an accumulator for maximum stability and determinism. This decouples physics from rendering rate.
```typescript
import { updateWorld } from "crashcat"
const FIXED_TIMESTEP = 1 / 60 // 60 Hz
let accumulator = 0
// In your game loop:
const deltaTime = getDeltaTime() // Time since last frame in seconds
accumulator += deltaTime
while (accumulator >= FIXED_TIMESTEP) {
updateWorld(world, listener, FIXED_TIMESTEP)
accumulator -= FIXED_TIMESTEP
}
const alpha = accumulator / FIXED_TIMESTEP // Interpolation factor for rendering
```
--------------------------------
### Enable Continuous Collision Detection (CCD)
Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md
Shows how to enable Continuous Collision Detection (CCD) for a rigid body. CCD is crucial for fast-moving objects like bullets or vehicles to prevent them from tunneling through thin walls.
```typescript
rigidBody.setCcdEnabled(body, true);
```
--------------------------------
### Add Force and Impulse to Rigid Body
Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md
Shows how to apply forces and impulses to a rigid body. Forces accumulate until the next physics step, while impulses provide instant velocity changes. Use `addForceAtPosition` or `addImpulseAtPosition` to induce rotation.
```typescript
rigidBody.addForceAtPosition(body, new Vector3(0, 100, 0), new Vector3(0, 0, 1));
rigidBody.addImpulseAtPosition(body, new Vector3(0, 50, 0), new Vector3(0, 0, -1));
```
--------------------------------
### Read Rigid Body Mass Properties
Source: https://github.com/isaac-mason/crashcat/blob/main/README.md
Shows how to read the mass and inverse mass from a rigid body's motion properties.
```typescript
// read mass properties
const mass = 1 / body.motionProperties.invMass; // mass in kg
const invMass = body.motionProperties.invMass; // 1/mass, used internally
```
--------------------------------
### Control Rigid Body Sleeping
Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md
Illustrates how to control the sleeping state of rigid bodies. Sleeping improves performance by skipping simulation for bodies that are at rest.
```typescript
rigidBody.sleep(body);
rigidBody.wakeUp(body);
```
--------------------------------
### Cast Ray Through World
Source: https://github.com/isaac-mason/crashcat/blob/main/README.md
Demonstrates casting a ray to detect collisions with bodies in the world. Useful for line-of-sight checks and projectile trajectories. It shows how to use 'closest', 'any', and 'all' collectors.
```typescript
// cast a ray through the world to find bodies
const rayOrigin = vec3.fromValues(0, 5, 0);
const rayDirection = vec3.fromValues(0, -1, 0);
const rayLength = 100;
// create a filter to control what the ray can hit
const queryFilter = filter.create(world.settings.layers);
// closest: finds the nearest hit along the ray
const closestCollector = createClosestCastRayCollector();
const raySettings = createDefaultCastRaySettings();
castRay(world, closestCollector, raySettings, rayOrigin, rayDirection, rayLength, queryFilter);
if (closestCollector.hit.status === CastRayStatus.COLLIDING) {
const hitDistance = closestCollector.hit.fraction * rayLength;
const hitPoint = vec3.scaleAndAdd(vec3.create(), rayOrigin, rayDirection, hitDistance);
const hitBody = rigidBody.get(world, closestCollector.hit.bodyIdB)!;
const surfaceNormal = rigidBody.getSurfaceNormal(vec3.create(), hitBody, hitPoint, closestCollector.hit.subShapeId);
console.log('closest hit at', hitPoint);
console.log('surface normal:', surfaceNormal);
}
// any: finds the first hit (fast early-out, useful for line-of-sight checks)
const anyCollector = createAnyCastRayCollector();
anyCollector.reset();
castRay(world, anyCollector, raySettings, rayOrigin, rayDirection, rayLength, queryFilter);
if (anyCollector.hit.status === CastRayStatus.COLLIDING) {
console.log('ray hit something');
}
// all: collects every hit along the ray
const allCollector = createAllCastRayCollector();
allCollector.reset();
castRay(world, allCollector, raySettings, rayOrigin, rayDirection, rayLength, queryFilter);
for (const hit of allCollector.hits) {
if (hit.status === CastRayStatus.COLLIDING) {
console.log('hit at fraction', hit.fraction);
}
}
```
--------------------------------
### Create Rigid Body with Explicit Mass Properties Override
Source: https://github.com/isaac-mason/crashcat/blob/main/README.md
Explains how to completely override mass properties, which is particularly useful for triangle meshes. It shows setting mass and inertia for a box shape.
```typescript
// completely override mass properties (useful for triangle meshes)
const triangleMeshMassProperties = massProperties.create();
massProperties.setMassAndInertiaOfSolidBox(triangleMeshMassProperties, [2, 2, 2], 1000); // boxSize, density
const dynamicTriangleMeshBody = rigidBody.create(world, {
shape: box.create({ halfExtents: [1, 1, 1] }),
motionType: MotionType.DYNAMIC,
objectLayer: OBJECT_LAYER_MOVING,
massPropertiesOverride: triangleMeshMassProperties,
});
```
--------------------------------
### Create a Sphere Shape
Source: https://github.com/isaac-mason/crashcat/blob/main/README.md
Use sphere.create to define a sphere. You can specify the radius and density for mass calculation.
```typescript
// simplest and fastest convex shape
sphere.create({ radius: 1 });
// with density for mass calculation
sphere.create({ radius: 1, density: 1000 }); // kg/m³
```
--------------------------------
### Read and Set Rigid Body Velocities
Source: https://github.com/isaac-mason/crashcat/blob/main/README.md
Shows how to access the linear and angular velocities of a dynamic body and how to modify them using the rigid body API.
```typescript
// read velocities
const linearVelocity = dynamicBody.motionProperties.linearVelocity; // [x, y, z] in m/s
const angularVelocity = dynamicBody.motionProperties.angularVelocity; // [x, y, z] in rad/s
// set linear velocity
igidBody.setLinearVelocity(world, dynamicBody, [5, 0, 0]); // shoot to the right
// set angular velocity
igidBody.setAngularVelocity(world, dynamicBody, [0, 1, 0]); // spin around y-axis
```
--------------------------------
### Move Kinematic Rigid Body
Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md
Illustrates moving a kinematic rigid body to a target position and rotation using `moveKinematic`. This method computes necessary velocities for smooth interaction with dynamic bodies, avoiding teleportation.
```typescript
rigidBody.moveKinematic(body, new Vector3(5, 5, 5), new Quaternion(0, 0, 0, 1));
```
--------------------------------
### Variable Timestep Simulation Update
Source: https://github.com/isaac-mason/crashcat/blob/main/README.md
Advances the physics simulation using the frame's delta time, suitable for simple use cases. Ensure delta time is capped to prevent instability.
```javascript
let lastTime = performance.now();
const maxDelta = 1 / 30;
function gameLoopVariableTimestep() {
const currentTime = performance.now();
const delta = Math.min((currentTime - lastTime) / 1000, maxDelta);
lastTime = currentTime;
updateWorld(world, undefined, delta);
// ... render ...
requestAnimationFrame(gameLoopVariableTimestep);
}
```
--------------------------------
### Register All Shapes and Constraints
Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md
Conveniently registers all built-in shapes and constraints for use in the physics world. This approach includes all features, potentially increasing bundle size.
```typescript
import { registerAll } from '@crashcat/physics';
registerAll();
```
--------------------------------
### Object Pooling for Rigid Bodies
Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md
Illustrates how rigid bodies are pooled internally for performance. It's recommended to store body IDs instead of direct references to avoid issues with invalidated IDs when bodies are removed and reused.
```typescript
const bodyId = body.id;
// ... later, to retrieve the body:
const rigidBody = rigidBody.get(world, bodyId);
```
--------------------------------
### Set Rigid Body Motion Types
Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md
Shows how to set the motion type for a rigid body. Choose 'static' for immovable objects, 'dynamic' for objects affected by physics, and 'kinematic' for objects controlled by script/animation.
```typescript
rigidBody.setMotionType(body, RigidBodyMotionType.Static);
```
--------------------------------
### Move Kinematic Body with Velocities
Source: https://github.com/isaac-mason/crashcat/blob/main/README.md
Use `moveKinematic` to compute velocities for kinematic bodies to reach a target position and quaternion. Prefer this over `setTransform` for smooth movement.
```typescript
const platform = rigidBody.create(world, {
shape: box.create({ halfExtents: [2, 0.2, 2] }),
motionType: MotionType.KINEMATIC,
objectLayer: OBJECT_LAYER_MOVING,
position: [0, 2, 0],
});
// move each frame by computing velocities
const deltaTime = 1 / 60;
const targetPosition = vec3.fromValues(5, 2, 0);
const targetQuaternion = quat.create();
quat.setAxisAngle(targetQuaternion, vec3.fromValues(0, 1, 0), Math.PI / 4);
rigidBody.moveKinematic(platform, targetPosition, targetQuaternion, deltaTime);
```
--------------------------------
### Set Rigid Body Maximum Velocities
Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md
Shows how to set maximum linear and angular velocities for a rigid body. This helps prevent instability and tunneling issues caused by extremely high speeds.
```typescript
rigidBody.setMaxVelocities(body, 100, 10);
```
--------------------------------
### Attach User Data to Rigid Bodies
Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md
Attach custom game-specific data to rigid bodies for easy lookup during collision callbacks.
```typescript
const body = world.createRigidBody(rigidBodyDesc);
body.userData = {
level: 1,
health: 100
};
```
--------------------------------
### Initialize and Update Three.js Debug Renderer
Source: https://github.com/isaac-mason/crashcat/blob/main/README.md
Initializes the debug renderer with default options and updates it each frame after the physics step. Ensure the debug renderer's object3d is added to your Three.js scene.
```typescript
import type { World } from 'crashcat';
import { debugRenderer } from 'crashcat/three';
import type * as THREE from 'three';
declare const scene: THREE.Scene;
declare const world: World;
// create debug renderer with default options
const options = debugRenderer.createDefaultOptions();
const state = debugRenderer.init(options);
// add to scene
scene.add(state.object3d);
// update each frame after physics step
function animate() {
// ... update physics ...
debugRenderer.update(state, world);
// ... render scene ...
}
```
--------------------------------
### Set Rigid Body Damping
Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md
Illustrates setting linear and angular damping for a rigid body. Higher damping values simulate increased air resistance or drag, causing the body to slow down faster.
```typescript
rigidBody.setDamping(body, 0.5, 0.2);
```
--------------------------------
### Customize Three.js Debug Renderer Visualization Options
Source: https://github.com/isaac-mason/crashcat/blob/main/README.md
Customizes which physics simulation elements are visualized and their appearance. Options include body visualization, contact points, constraints, and broadphase details.
```typescript
const customOptions = debugRenderer.createDefaultOptions();
/* body visualization options */
customOptions.bodies.enabled = true;
customOptions.bodies.wireframe = false;
customOptions.bodies.showLinearVelocity = false;
customOptions.bodies.showAngularVelocity = false;
// unique color per body instance
customOptions.bodies.color = debugRenderer.BodyColorMode.INSTANCE;
// color by motion type (static, dynamic, kinematic)
customOptions.bodies.color = debugRenderer.BodyColorMode.MOTION_TYPE;
// color by sleeping state
customOptions.bodies.color = debugRenderer.BodyColorMode.SLEEPING;
// color by simulation island
customOptions.bodies.color = debugRenderer.BodyColorMode.ISLAND;
/* contact points options */
customOptions.contacts.enabled = true;
/* contact constraints options */
customOptions.contactConstraints.enabled = true;
/* constraints options (hinges, sliders, etc.) */
customOptions.constraints.enabled = true;
customOptions.constraints.drawLimits = true;
customOptions.constraints.size = 0.5;
/* broadphase options */
customOptions.broadphaseDbvt.enabled = false;
customOptions.broadphaseDbvt.showLeafNodes = true;
customOptions.broadphaseDbvt.showNonLeafNodes = true;
/* triangle mesh bvh options */
customOptions.triangleMeshBvh.enabled = false;
customOptions.triangleMeshBvh.showLeafNodes = true;
customOptions.triangleMeshBvh.showNonLeafNodes = true;
```
--------------------------------
### Create a Triangle Mesh Shape
Source: https://github.com/isaac-mason/crashcat/blob/main/README.md
Triangle meshes are used for complex static geometry like terrain. They are created using positions and indices. For dynamic meshes, mass properties must be provided explicitly.
```typescript
// triangle mesh: for complex static geometry like terrain
const meshShape = triangleMesh.create({
positions: [-10, 0, -10, 10, 0, -10, 10, 0, 10, -10, 0, 10],
indices: [0, 1, 2, 0, 2, 3],
});
// triangle meshes typically used with static bodies
rigidBody.create(world, {
shape: meshShape,
motionType: MotionType.STATIC,
objectLayer: OBJECT_LAYER_NOT_MOVING,
});
// for dynamic triangle meshes, provide mass properties explicitly
const dynamicMeshProps = massProperties.create();
massProperties.setMassAndInertiaOfSolidBox(dynamicMeshProps, [2, 2, 2], 1000);
rigidBody.create(world, {
shape: meshShape,
motionType: MotionType.DYNAMIC,
objectLayer: OBJECT_LAYER_MOVING,
massPropertiesOverride: dynamicMeshProps,
});
```
--------------------------------
### Configure Linear and Angular Damping
Source: https://github.com/isaac-mason/crashcat/blob/main/README.md
Set linear and angular damping to simulate air resistance or drag. Higher values cause objects to slow down faster. Damping can be set during creation or modified afterward.
```typescript
// linear damping reduces linear velocity over time (0-1, higher = more drag)
const dampedBody = rigidBody.create(world, {
shape: sphere.create({ radius: 1 }),
motionType: MotionType.DYNAMIC,
objectLayer: OBJECT_LAYER_MOVING,
linearDamping: 0.5, // default is 0.05
angularDamping: 0.8, // default is 0.05
});
// damping can also be modified after creation
dampedBody.motionProperties.linearDamping = 0.2;
dampedBody.motionProperties.angularDamping = 0.2;
```