Skip to content

pc-joint silently creates no constraint when entity-a or entity-b does not resolve #389

Description

@willeastcott

entity-a and entity-b resolve through getEntity, which returns null for a reference that names nothing live. pc-joint passes that null straight to the engine component, which declines to create a constraint. Nothing is reported, so the joint just does not exist — and since a joint has no visual of its own, the only symptom is that two bodies move independently. That is indistinguishable from a physics problem, which is where the debugging time goes.

Where it bites

The reference need not be wrong. It resolves once, when it is assigned, and a pc-node has no entity until its container asset has loaded — so this is silent and empty:

<pc-entity name="knee">
    <pc-joint type="hinge" entity-a="#shin" entity-b="#thigh"></pc-joint>
</pc-entity>

<pc-model asset="robot">
    <pc-node id="shin" name="shin_l"><pc-rigidbody type="dynamic"></pc-rigidbody></pc-node>
    <pc-node id="thigh" name="thigh_l"><pc-rigidbody type="dynamic"></pc-rigidbody></pc-node>
</pc-model>

Hit while building the ragdoll example in #388. The workaround there was to nest each joint inside the pc-node it drives, which is a better pattern anyway — a pc-node's children are built once it has bound, so the references resolve — but nothing pointed at it, and nothing said the first attempt had failed.

This is deliberate

Worth being explicit, because it changes the question from "fix an oversight" to "revisit a decision". The current behaviour is pinned by a test in test/integration/components/joint-component.test.ts:

it('leaves an unresolvable reference null without warning', ...)
// No warning claimed here: getEntity resolves references quietly, and the guard fails the test if anything warns

There is a plausible reason for it. The documented recovery is to assign the attribute again once the target exists, so an author who builds a scene from script may legitimately set a reference before its target is there, and a warning at that moment would be noise.

Against that: sibling elements already report exactly this class of mistake, and loudly. pc-model warns for an asset id that resolves to nothing (pc-model could not find asset 'x' - model not created). pc-node warns for a name it cannot find, for an ambiguous one, and for a duplicate binding — it even offers the closest match by edit distance and exposes the outcome as state. Against that backdrop pc-joint's silence reads as an inconsistency rather than a policy.

Two failures, two fixes

If this is reported, the cause matters, because the remedies are opposite:

  • Nothing in the document matches the reference. Usually a typo or a missing element. Fix the reference.
  • Something matches but is not backing an entity. Usually timing — the pc-node case above — or pointing at the wrong element entirely. The reference may be perfectly correct.

getEntity currently collapses both into null. Distinguishing them needs the matched element, which means splitting the element lookup out of getEntity.

Scope

Five components resolve references this way: pc-joint, pc-button (image-entity), pc-scrollbar (handle-entity), pc-scrollview (viewport-entity, content-entity, the scrollbars) and pc-script (entity: attribute values). They all fail the same silent way. Worth deciding whether this is a pc-joint fix or a shared one — pc-joint is only the most punishing case, because the others usually have some visible consequence.

Options

  1. Warn on a non-empty reference that does not resolve, naming which of the two causes it hit. Non-breaking, no timing change. Accepts the false positive on the documented "assign it again later" pattern, which the message can turn into guidance rather than noise.
  2. Warn only if it is still unresolved a frame later. Removes that false positive at the cost of a deferred, slightly less predictable message.
  3. Re-resolve automatically when the referenced element becomes ready, and warn only if it never does. Fixes the underlying problem rather than reporting it, and pc-node already has the machinery (it follows its host's ready events). But it changes when references resolve, so it wants more care than a diagnostic — and it is the one option that could surprise existing code.
  4. Expose the outcome as state, as pc-node does, so a document's wiring can be asserted programmatically rather than only observed in the console.

An empty reference should stay silent under any of these: on entity-b it is the documented way to pin the first body to a point in world space, and on either it is the transient state of an element whose reference has yet to be assigned.

Happy to put up a PR for whichever direction is preferred. I have (1) implemented on a local branch, including the split of findEntityElement out of getEntity and updated tests, if that is the one worth having.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions