Dicey Table

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 Static is what keeps a triangle mesh.
  • Sidecars — the collider list and triggers keys 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 APIObjectPhysics, the read-only mod view of every field on this panel.