Skip to content

dh.collider

Component id: dh.collider
Wire id: 9

dh.collider is the description of collision. There is no map-wide collider list, no per-kind collision field and no second reader: a model part’s override, a boxBrush and a plain object record each carry one dh.collider in their components list, and the server’s install, the client’s prediction, the editor and the Unity scene builder all read that one description through one enumeration.

OwnerWritten onShapes are in
A model partThe part’s override in the world instance’s childrenThe part’s own frame: its compiled pivot, rotation and scale
A boxBrushThe brush’s componentsThe brush’s frame, with its size as the box (a brush’s scale never scales it)
A plain objectThe object’s componentsThe object’s own frame, from its position, rotation and scale

An object with no position (position omitted) stands at the origin, so its shapes are in world meters. That is how an importer writes the collision of a map it did not model: one object named collision, holding every shape.

Nothing else takes the component yet. The logic kinds and the physics props keep their own bodies until their kinds become components.

FieldTypeDefaultDescription
state"solid" | "off" | "trigger""solid"Solid blocks. Trigger collides with nothing and only overlaps; a dh.trigger on the same owner is what fires touch edges. Off collides with nothing at all.
wasTriggerboolfalseMeaningful only with "off": whether switching it back on restores a trigger rather than a solid.
materialstringthe world defaultThe map material slot whose surface the shapes report: footsteps, the landing value, wall hits and friction. A key the map’s materials lacks is a compile error.
shapesarrayone auto shapeWhat it collides with. Omitted means one auto shape built from the owner’s own mesh. An explicit [] collides with nothing.

state is a field like any other: the editor’s collider toggle writes it, a scene edit replicates it, a late joiner reads it from the same fields list as everyone else, and a save writes it into the owner’s dh.collider.

Every shape carries a type and only the fields that type owns; a field the type does not own is an error naming it.

typeFieldsNotes
boxcenter, halfExtents, rotationrotation is euler degrees applied Z, then Y, then X.
spherecenter, radius
capsulecenter, radius, halfHeight, rotationhalfHeight is half the distance between the two end centers, along the capsule’s local +Y.
hullpointsA convex hull over a point cloud, each point [x, y, z] in the owner’s frame. A cloud over the physics backend’s vertex budget is simplified, never refused; a flat or too-small cloud is skipped with a warning naming the owner and shape.
meshnodeCollides as the triangles of a part of the same model. node names a part by path; omitted, it is the owner’s own.
automethodOne shape built from the owner’s render mesh, by method: default, box, hulls or mesh.

Any shape may carry its own material, which wins over the component’s. Every shape takes the owner’s transform, scale included: a box with half extents [1, 1, 1] on a part scaled [2, 1, 1] is two meters out on its local X.

{
"$type": "object", "id": "5b1c2f30-0000-4000-8000-0000000000c1", "name": "collision",
"components": [
{ "$type": "dh.collider", "material": "concrete", "shapes": [
{ "type": "box", "center": [0, -0.5, 0], "halfExtents": [16.5, 0.5, 16.5] },
{ "type": "sphere", "center": [4, 1, 4], "radius": 1 },
{ "type": "hull", "points": [[0, 0, 0], [2, 0, 0], [1, 0, 2], [1, 2, 1]] }
] }
]
}

Collision is explicit in a compiled map. The runtime reads components and never an authored default, so what the compiler leaves in the pallet is what collides.

  • autoCollision stays the map default for a world’s parts. A part that authors no collider gets one the compiler builds from the mode, marked "generated": true.
  • An authored auto shape is replaced by what it produced, marked "auto": true, so an edit can turn it back. A brush’s auto shape is always its exact box.
  • A brush with no component gets a generated collider over its exact box, in every mode, with the brush’s own material.
  • Hull points are written in the part’s local frame, so the shape follows the part when it is moved.

generated and auto are the compiler’s own marks: authored in a source file, either is an error.

The Inspector draws a dh.collider as a generated card (state, surface and a list of shapes, each with the fields its type owns) and draws every selected owner’s shapes as wireframes in the world, faint for a collider that is off. A shape list written in the card replaces the owner’s list whole: the server rebuilds that one owner’s bodies from it and every client’s prediction builds from the same list, so nobody walks through a shape the server no longer has. A mesh shape can be neither added nor moved there, because it is cooked when the map compiles.

A plain object that has no collider is offered one by Add Component, with a half meter box. Taking a collider off a plain object takes its bodies away; taking it off a part or a brush puts back the collider the compiled map gave it.

Every collider edit saves, into the components of whatever states the owner: a part’s override, a brush’s record, a plain object’s record. One writer does it for every owner, and it writes only what changed:

  • A field is written under its own key, rewritten in place where the component already spells it, inserted after its last member where it does not. Every other byte of the file stays as it was, comments and alignment included. A field written back to its default (solid, a wasTrigger of false) is taken out rather than spelled, and wasTrigger is written only beside off.
  • A shape list is written whole, since it is edited whole: one shape a line, the way a hand-written map spells one. A shape an auto shape produced is written as the shape it is, without the compiler’s mark. A part whose collider the map generated gains the surface that collider wore along with its shapes, so it rebuilds wearing the same one.
  • A component the owner did not state is added to its components (and a part with no override gains one). Adding a collider to a plain object writes its shapes.
  • Removing a collider from a plain object takes the component out of its record, and the components member with it when nothing else is left. Removing one from a part or a brush puts back what its map states: a component that states nothing more is taken out, and a part’s override left saying nothing but its address is taken out whole.

After a save, what the file states is the baseline the marks are measured against, for everyone in the room, and a rebuild of the saved source builds the bodies the session was standing on.

A trigger collider fires nothing by itself. Touch edges are the outputs of a dh.trigger on the same owner, which measures the owner’s one box shape and can be switched on and off; connections over onStartTouch on an owner with no dh.trigger is a build error.

A water surface is the common case: a water part whose collider is "trigger" becomes a water body the mover swims in, not a floor.

A mesh shape is cooked by the build, in the engine, into the map’s .dh-colcook companion (format 5). Each entry is keyed by the owner’s part scene id and the shape’s index, so a part that moves, is renamed or is re-parented keeps its cook, two shapes of one owner cook apart, and each placement of a model has entries of its own while sharing its blobs. The cook leaves out every owner whose state is not solid and every owner whose override says enabled: false; the runtime restores such a part from the stub the cook ships for it.