the symptom, in your words

"Per-shape collision_tags in a vmdl are inert at runtime on a ModelCollider"

✓ verified on 26.07.15a
lane Getting art inposted

▸ SYMPTOM

You author a model with several physics hulls and give each hull its own collision_tags in the vmdl's PhysicsShapeList — say a tight "solid" trunk hull and a big "canopy" hull — expecting to trace against them selectively at runtime with Scene.Trace.WithoutTags("canopy").

At runtime the filter does nothing. Every hull behaves identically. A wide canopy hull acts fully solid: it blocks the character mover metres away from the trunk and absorbs projectile traces aimed at anything standing underneath. The per-shape tags you authored are simply not there.

▸ CAUSE

A scene ModelCollider never reads per-shape collision_tags. Two engine steps drop them:

  1. ModelCollider.CreatePhysicsShapes builds the physics shapes from the model, reading only Surface and BoneIndex — it never looks at collision_tags.
  2. Collider.ConfigureShapes then runs shape.Tags.SetFrom(GameObject.Tags) over every shape, and OnTagsUpdatedInternal repeats that whenever the GameObject's tags change.

So each shape ends up carrying the collider GameObject's tags, not the tag you authored on the shape. If the GameObject is untagged, every hull reads as plain solid with no tags at all — which is exactly why WithoutTags can't peel one hull off from another.

(Verified against engine source sbox-public master — Collider.cs around line 450.)

▸ FIX

If you need per-class trace filtering, put the hulls on separate collider GameObjects, each with its own GameObject.Tags. GameObject tags do surface to the shapes (that's the very mechanism overwriting the per-shape ones), so one GameObject tagged solid and another tagged canopy gives you the per-hull filtering you wanted:

snippet
 TreeModel (GameObject)
├── Trunk        ModelCollider, GameObject.Tags = "solid"
└── Canopy       ModelCollider, GameObject.Tags = "canopy"

Now Scene.Trace.WithoutTags("canopy") hits the trunk and skips the canopy.

Often the simpler shipped fix is to question whether the extra hull should collide at all. A tree canopy is usually render-only — dropping the canopy hulls entirely (tight trunk hull, foliage as pure visuals) removes the false-solid problem without any tag gymnastics.

▸ WHY IT WORKS

Tags are a GameObject-level concept in the scene system; the physics shapes inherit the GameObject's tag set at configure time and any per-shape authoring is discarded before it ever reaches the trace. Splitting into separate colliders gives each hull its own GameObject, so each carries a distinct tag set that survives to the shape — the only level at which the runtime actually keeps a tag.

Verified on engine 26.07.15a: seen in a real project.
s&box moves fast; an undated fix is a liability. Spot a stale detail?
changelog
  • Published

Want to know when new guides or fixes drop? Join the community to help build this out. Report gotchas, flag outdated fixes, or just lurk.

Join the Discord