COLLISION
COLLISION is an Engine component section (tone="engine"), intrinsic to every table
Entity, paired with RIGIDBODY directly above it. It
also carries a real header enable checkbox, backed by physics.collisionEnabled.
Source: the COLLISION EmCollapsible in TableEditModeShell.tsx's object Inspector
branch; schema in packages/shared/src/tableObjects.ts (objectPhysicsSchema).
The enable checkbox#
| Control | Type | Default | Runtime effect | Replicates |
|---|---|---|---|---|
| Header checkbox | boolean | true (absent = enabled) |
Disabling it makes the entity pass-through while staying visible and grabbable — it keeps its render and rigidbody, it stops colliding with anything. | Yes — physics.collisionEnabled, part of TableObjectState.physics |
Shape#
| Field | Type | Values | Default | Runtime effect | Replicates |
|---|---|---|---|---|---|
| Shape | select | Auto, Box, Sphere, Capsule, Cylinder, Convex Hull, Mesh |
per-kind (box for card/token/deck/bag/board/card-holder, convexHull for die, auto for custom) |
Auto keeps the runtime's current per-kind collider behavior. Box/sphere/capsule/cylinder are cheap primitive approximations fitted to the model's bounds — fastest, good for most objects. Convex hull is an accurate-ish hull of the mesh — slower, needed for faithful die tumbling. Mesh is the exact triangle mesh — most accurate and slowest, and only valid for static bodies in most engines. |
Yes — physics.collisionShape |
The Inspector's own hint: "Box, sphere & capsule are fastest; convex hull and mesh are more accurate but slower (mesh is best for static bodies)."
⚠ The Default column is the Inspector's placeholder, not what the runtime builds.
createCollisionConfig (apps/web/src/playcanvas/physics/collisionHelpers.ts) returns a box for
every kind, fitted to the entity's scale. A die gets its hull by a separate route:
applyDieConvexHullShape swaps a convex-hull shape onto the body only when the kind is die
and metadata.standardPresetId names one of the hulls in DIE_CONVEX_HULL_VERTS. A die
built from your own imported model keeps the box, and tumbles like one — set Convex Hull
explicitly if you need faithful tumbling from a custom die.
The collision box editor — custom/imported models only#
Shown only for a custom/imported model (metadata.customModelAssetId set) whose shape is
Auto or Box (showCollisionBoxEditor in the shell).
| Field | Type | Range / units | Default | Runtime effect | Replicates |
|---|---|---|---|---|---|
| Size | vector3 | positive, object-local units (measured at scale = identity) | auto-fit to the model's measured bounds | The collider's full dimensions before the object's own scale is baked in at realize time. | Yes — physics.collisionSize |
| Offset | vector3 | object-local units | auto-fit center | The collider box's center offset, paired with Size. | Yes — physics.collisionOffset |
Two buttons accompany the editor:
- Edit Collision Box — toggles a draggable box gizmo in the viewport; use the Move/Scale gizmo tools to reshape it directly rather than typing numbers.
- Reset to fit — clears any authored override and returns to the auto-fit measurement (disabled when there is no override to reset).
While bounds are still being measured, the editor shows "Measuring model bounds…" instead of the size/offset fields.
Size and Offset are only meaningful for box-shaped colliders — they are ignored for every
other Shape value, including Auto on a non-custom object.
More than one shape per model: the authored collider list#
This panel holds one shape per Entity, because physics.collisionShape is one field of
replicated state. A model asset can carry more than that: its .meta.json sidecar (for a mod
or project model) or its preset override (for a platform model) may author a list of up to eight
collider entries, each with its own shape and its own offset / rotation / scale, which the
runtime assembles into one compound collider at spawn.
The shape vocabulary is the same six values as the Shape select above — the enum did not grow, and
a decomposed collider is still convexHull. What the list adds is (a) a per-entry transform, so a
shape can sit anywhere on the model rather than only at its measured centre, and (b) several shapes
at once, which is how a concave model gets an honest collider without a triangle mesh: three boxes
around an arch cost far less than the arch's geometry and still let a piece pass under it.
A convexHull or mesh entry can also take its geometry from a baked collider GLB rather than
from the render mesh — a sibling file named <model-stem>.collider.glb. Bake one when the render
mesh is too heavy to collide against: a collider mesh is budgeted at 2,000 triangles, far below
the render budget, because the hull builder feeds every vertex of the source into the shape with no
reduction at all and each one costs time per contact.
Author the list in the Model tab's COLLISION panel — select the Collision row in the
model tree. That panel has the shape select, the per-shape numeric fields in feet, Fit to bounds, the per-entry Offset / Rotation / Scale, the bake and decimation block, and
+ Add collider / Duplicate / Delete. A one-entry list renders there with no list chrome at
all, so the common case looks exactly like a single collider. See
Item editors: Model § COLLISION, and
Sidecars § Model meta sidecars
for the JSON the panel writes.
⚠ Only one primitive entry is ever realised. A PlayCanvas collision component is a single
shape with no compound-of-primitives form, so in a list mixing mesh-derived and several
primitive entries, only the first primitive is built. Mesh-derived entries ride one collider
file and are realised in full — that is how a decomposed hull works — so several hulls are
fine and several boxes are not. The Model tab names the entries that will not be built rather
than dropping them silently.
Whatever the list says, this panel keeps showing one recorded shape per Entity — the first entry's — because that is the field that replicates.
The two refusals: when a mesh-derived collider does not happen#
convexHull and mesh are built from geometry, and geometry is not always there or not always
usable. Two conditions change or defer what the runtime builds, and each one leaves the previous
shape in place rather than failing loudly. Knowing them is the difference between "my collider is
wrong" and "my collider was refused".
1. A triangle Mesh collider cannot be dynamic — it falls back to Convex Hull. Ammo's
triangle-mesh shape is valid only for a static body, so a mesh request on a dynamic Entity is
downgraded to a convex hull, silently. A hull is the model's outer shell: every dent, hole and
concavity is filled in, which is exactly the property a mesh collider was chosen to avoid. To keep
a genuine triangle mesh, set RIGIDBODY Type to Static.
If the object must move and must be concave, author a multi-entry collider list of primitives
instead.
2. Geometry that has not loaded yet is a pending state, not an error. Until the GLB attaches
there is no mesh to build from, so the request is skipped and re-applied the moment the model
arrives. A Mesh or Convex Hull selection that appears to do nothing for a beat is usually this,
and it resolves itself.
This used to be three. A transform sitting between the model and the collidable — which the runtime creates itself, both when it squashes a piece's model into a unit cube and when it recentres an imported GLB on the object origin — used to refuse a mesh-derived collider outright, because PlayCanvas builds a mesh shape from raw vertices and discards anything between them and the collidable. The runtime now re-expresses those vertices in the collidable's own frame instead, so a nested or normalised model gets a faithful mesh collider like any other, and nothing needs authoring around it. Models exported at unit extent skip the extra vertex copy, which is the only reason to still prefer them.
Both now render in the Model tab's COLLISION panel, before you commit — that is most
of what the model editor was built for. Reason 2 is styled as a pending state rather than a
warning, because it resolves itself. On the table itself they remain silent, so if a piece
behaves as though its collider were a different shape than the one selected, read this list
before assuming a bug.
⚠ Auto is not "no collider". It is the per-kind default, and the model tree shows it as a
real (greyed, italic) row for exactly that reason. There is no state in which a piece has no
collider because nobody authored one.
See also#
- rigidbody.md — the paired intrinsic component, and why
Staticis what keeps a triangle mesh. - Sidecars — the
colliderlist andtriggerskeys a model asset can author, field by field. - Item editors: Model — the panel that authors the list, the collider triangle budget, and
Re-apply to N objects. - piece.md — where the collision shape's suggested per-kind default is derived from.
- Mod scripting API —
ObjectPhysics, the read-only mod view of every field on this panel.
