Skip to content

Usability fixes from a demolition demo - #13

Merged
Miguel249 merged 4 commits into
mainfrom
docs/usability-fixes
Sep 23, 2026
Merged

Miguel249 merged 4 commits into
mainfrom
docs/usability-fixes

Conversation

@Miguel249

Copy link
Copy Markdown
Owner

Four points found while building a demolition demo on Box3D.NET 0.5.0. Two are documentation, two are API ergonomics. All changes are additive; package validation against the previous release passes with and without the iOS target.

  1. Explode said "only spheres, capsules and hulls respond", which reads as "boxes do not". Boxes are hulls and do respond. The remarks now say so, and explain, from b3World_Explode, why meshes, height fields and compounds never respond: they are static-only and the blast queries only the dynamic tree. They also say that kinematic bodies are not pushed and that sleeping ones are woken. AddBox and ShapeType.Hull note that a box reports ShapeType.Hull. New ExplosionTests.
  2. Mass ratios across joints. Reproduced with ten spherical joints: at ~1450:1 the links reach 427 m/s; at ~20:1 the chain holds. docs/guides/joints.md gets a callout with the measurement, and the chain sample has a comment where its links are created.
  3. ShapeDefinition.Friction / Restitution: init shortcuts over Material, instead of With… methods, since the API is built entirely from with expressions and Shape already has the same shortcuts. There is no backing field, so equality is unchanged. New ShapeDefinitionTests.
  4. Explode's filter is documented as a category mask that is checked in one direction only. It is not renamed, which would break callers passing it by name. No QueryFilter overload was added, because it would have to silently ignore Categories.

Local: 378/378 tests, 0 warnings, dotnet format clean, 16/16 samples, api-coverage -Check up to date.

Miguel Angel Charris Carmona and others added 4 commits September 23, 2026 15:29
The remarks on Explode read "Only spheres, capsules and hulls respond." In
this API Box and ConvexHull are different types, so that reads as "boxes do
not", and a user building a demolition demo built every crate from eight
corners with ConvexHull.FromPoints to get around a limitation that does not
exist: AddBox makes a hull through b3MakeBoxHull, and a hull is what Box3D
pushes.

The remarks now say that boxes respond because they are hulls, and why
meshes, height fields and compounds do not. b3World_Explode queries only the
dynamic-body tree, and those three can only be attached to static bodies;
b3GetShapeProjectedArea would also give them no area. The same query is why
kinematic bodies are not pushed, which is now stated too.

AddBox and ShapeType.Hull say that a box reports ShapeType.Hull, so reading
Shape.Type back is not another surprise.

ExplosionTests is new and pins each claim: boxes, spheres and capsules are
pushed; kinematic bodies and bodies out of reach are not; a sleeping body is
woken; and the filter is compared with shape categories.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A demolition demo hung a ball of about 33 kg from a chain of spherical
joints whose links weighed about 0.02 kg, and the links flew off. Nothing
in the library is wrong there: the solver resolves joints iteratively, and a
light body between an anchor and a heavy one barely moves the heavy one, so
the error never converges. But nothing said to avoid it either.

Reproduced before writing it down, with ten spherical joints: at about
1450:1 the fastest link reaches 427 m/s and the last joint stretches by
0.66 m; at about 20:1 the fastest link moves at 3.1 m/s and the joint holds
to 3 mm. The guide gets a callout recommending 10:1 to 20:1 or less, the
measurement behind it, and a note that the ratio that matters is the one
across each joint rather than the chain's total mass.

The chain sample says the same where its links are created, since that is
the code people copy. The measurement is not a test: it pins solver
behaviour, which is Box3D's to change, not this binding's.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Changing one material property on a shape definition took two nested with
expressions:

    ShapeDefinition.Default with { Material = PhysicsMaterial.Default with { Friction = 0.7f } }

ShapeDefinition.Friction and Restitution are init properties that read and
write Material, so the same thing is ShapeDefinition.Default with
{ Friction = 0.7f }.

Properties rather than WithFriction(float) methods, because nothing in this
API builds values through methods: every definition is a record with init
properties, adjusted with with, and a shortcut that did not work inside a
with expression would sit beside every other property as the one exception.
Shape already exposes Friction and Restitution as shortcuts for its material
at run time, so the definition now matches the handle.

Only these two. Every PhysicsMaterial property could get a shortcut, but
friction and restitution are the ones a game tunes per shape; the rest are
rarer, and duplicating the whole material would give ShapeDefinition two
ways to say everything.

The properties have no backing field, so equality and hashing are
unchanged: a definition built with a shortcut equals one built the nested
way, which ShapeDefinitionTests checks, along with initializer order and
that the values reach the native shape. The change is additive, so package
validation against the previous release is unaffected.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Explode takes ulong filter = ulong.MaxValue and assigns it to
b3ExplosionDef.maskBits, while every query takes a QueryFilter?. The name
suggests the latter, and the documentation, "which shape categories are
affected", did not say how.

b3World_Explode passes the mask to the dynamic tree, which compares it with
each proxy's category bits. That is the job QueryFilter.CollidesWith does in
a query, but a query also requires the shape's own mask to accept the
query's categories (b3ShouldQueryCollide checks both directions), and an
explosion does not. The parameter now says all of this, and a test shows a
shape whose CollidesWith is zero is still pushed.

The parameter keeps its name: renaming it would break anyone passing it as
filter: in source. There is no QueryFilter overload either. It would have
to drop QueryFilter.Categories on the floor, and an overload that silently
ignores half its argument is worse than a mask parameter whose
documentation is clear.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@Miguel249
Miguel249 merged commit 19f7ef3 into main Sep 23, 2026
23 checks passed
@Miguel249
Miguel249 deleted the docs/usability-fixes branch September 23, 2026 20:54
@Miguel249 Miguel249 mentioned this pull request Sep 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant