‹›
Utility Scripts · Body Penetration

Debugging and Reject Reasons

First determine whether registration, admission or movement inside an existing relationship failed.

A public-function error is separate from a rejected hit candidate. If jolt_penetrator_tip returns undefined or a setter returns false, read jolt_get_error_message(). If the Tip exists, inspect its info, reject and all relationships after the physics update.

Text and geometry debug#

GML
// Enable once.
jolt_debug_enable(true);

// Draw GUI:
jolt_penetration_debug_text(16, 16);

// Existing 3D Draw pass:
jolt_debug_draw(JOLT_DEBUG_SHAPES | JOLT_DEBUG_PENETRATION);

jolt_penetration_debug_text(x=16,y=16) prints Tip/body IDs, enabled, reject, state, relationship count, target, has_entered, capture_source and depth. The geometry flag draws the Tip, entry point and channel axis/limit. Debug does not add forces or decide capture.

Complete reject-reason table#

These names correspond to the numeric native reject enum. The GML wrapper returns a number; it does not define a public JoltPenetrationReject enum. Do not confuse these values with JoltPenetrationState.

CodeNameWhat to check
0NoneNo current rejection; not itself an entry event.
1NotPenetrableThe target is not enabled for penetration.
2MaskMismatchmask and category_bits share no bit.
3AngleRejectedActual impact angle exceeds max_angle.
4TipTooFarNo suitable nearby hit in the search; normal in free space.
5OffAxisGeometry did not satisfy axis entry.
6LateralVelocityExcessive incompatible lateral motion.
7AngularVelocityIncoming relative spin or the allowed motion budget.
8AlreadyOccupiedThe Tip reached its capacity or already has this target. Read relationships first.
9PairNotCollidableSensor, layer or group rules for the pair.
10UnsupportedShapeUnsupported collision shape.
11UnsupportedMotionThe penetrator must be Dynamic; the target must not be Kinematic.
12IncompatibleConstraintAnother user constraint attached to the penetrator.
13CapacityFullTip, relationship or candidate-buffer capacity.
14DeepOverlapInitial intrusion beyond capture_overlap, the depth limit or an invalid ray start inside the target.
15InvalidConfigInvalid settings; check ranges and the error channel.
16CommitFailedRelationship creation did not complete; inspect rollbacks and body validity.
17MovingOutwardMotion is not inward along both the surface and positive axis.
18InvalidHandleStale Tip, body or world.
19RearmPendingInsufficient geometric clearance after extraction.
20CaptureSpeedA positive max_capture_axial_speed is below the incoming speed.
21IncompatibleChannelAxis incompatible with an existing channel on the same body.
22AwaitingContactWaiting for acceptable proximity/conditions. Does not by itself prove an intent exists.
23NoAxisSurfaceThe Tip-axis ray did not verify a finite target surface.
24ContactRegionInvalid front region, finite-surface/normal mismatch or another blocking overlap.

Troubleshooting sequence#

  • Tip creation fails: check local surface placement, a nonzero axis, supported Dynamic shape and parameter ranges.
  • Tip exists but never enters: check capture_model, target enabled, mask, layer, current angle and relative motion.
  • A relationship exists but is Captured: wait for actual nonnegative signed_depth; do not confuse this with registration failure.
  • The tool captures but does not move inward: inspect forward_force, mass, impact energy and max_depth.
  • Another hit fails: inspect RearmPending and whole-body clearance. Check that you are not treating an old relationship as current.

Metrics#

GML
var metrics = jolt_penetration_metrics();
if (is_struct(metrics)) {
    show_debug_message("Relationships: " + string(metrics.relationships));
    show_debug_message("Candidate drops: " + string(metrics.candidate_drops));
    show_debug_message("Event drops: " + string(metrics.event_drops));
}
FieldsMeaning
schemaStructure version, currently 1.
tips / relationshipsCurrent live counts.
tip_capacity / relationship_capacity / event_capacity / event_countCapacities 128 / 32 / 256 and current event-queue occupancy.
candidates / probes / valid_probesCumulative candidate and geometry-evaluation counts, not unique-hit counts.
commits / rollbacks / candidate_drops / event_dropsCreated connections, rolled-back creation attempts, capacity drops and lost events.
contact_queries / contact_captures / primed_cancelsContact queries, contact-assisted captures and cancellations before actual entry. contact_captures is not the total of all model 2 captures.
pre_update_us / probe_us / relationship_update_us / clearance_us / physics_us / contact_usTiming diagnostics in µs, populated with native profiling enabled. They may remain 0 by default; the public penetration API has no profiling switch.

Counters belong to the world and accumulate during its lifetime; compare two samples to measure an interval. Getters only read diagnostics. Metrics do not identify the exact first cause of every failed impact; record configuration and trajectory when reproducing a problem.

Verified operation contracts#

These contracts distinguish units, return values, errors and lifetime according to the current implementation. Check results: false, 0 and undefined can mean different things for different methods.

Symbol / ownerSignatureOperation contract
jolt_penetration_debug_textjolt_penetration_debug_text(x=16,y=16)Draws diagnostic text at x/y, default 16/16. Call in the appropriate Draw context; not history or a getter.
jolt_penetration_metricsjolt_penetration_metrics()Current schema:1 events/metrics snapshot of the managed world, undefined on invalid world/transport; not a physics mutation command.