physics: never collide a ball that was not hit-tested - #579
Merged
Merged
Conversation
A ball created in a trough exit kicker a hundred units below the playfield still left from playfield level in a standalone build, while most balls of the same session left from the kicker's height. Nothing in the code moves that kicker, and the trace cannot tell which of the two transforms-derived heights was wrong. Every created ball now logs its spawn position together with the kicker's local, playfield and world position, the capture logs where the ball ended up and which capture height the kicker state holds, and each kicker logs its capture height once at initialization. A kicker rotated by a rotator mech also keeps its height instead of dropping to playfield level, since the rotated position only carries x and y.
A new ball's collision event started out as the struct default, which reads as a hit with collider 0 at time 0. Balls held in a kicker skip the hit tests, so that event was never cleared, but the collision phase ran for every ball, held or not. A ball created in a kicker and frozen there before its first physics step therefore collided with whatever collider 0 happened to be. On a table where that is the playfield floor, the ball was pushed from the trough exit, a hundred units below the playfield, up to playfield level with its x and y untouched, and the kick that followed a tick later launched it over the apron. Whether the kick arrived in the same tick as the capture decided if it happened. New balls now start with no pending collision, and the collision and contact phases leave frozen balls alone, as VP does: a locked ball carries no hit object, and every hit object is consumed after its collision. The contact case is VPE's own: a ball captured by a kicker during the collision phase still had its earlier contacts resolved, whose position recovery could move it out of the kicker.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A ball created inside a kicker below the playfield (a modelled trough exit) was sometimes launched from playfield level instead of from the kicker, flying over the apron. Traced with the simulation tracer in a standalone build.
Root cause
A ball's collision event is a struct whose default value reads as a hit with collider 0 at time 0, and a new ball state was created without initialising it. Balls held in a kicker are skipped by the hit-test loop, which is where the event normally gets cleared, but the collision phase ran for every ball, frozen or not. A ball created in a kicker and frozen there before its first physics step therefore had collider 0's collide routine applied to it, without any hit test. On the affected table collider 0 is a floor at z 0, whose push-out lifted the ball from the trough exit to one radius above the playfield with x and y untouched. When the kick arrived a tick later, it launched the ball from there. Whether the kick landed in the same tick as the capture decided if it happened, which is why it was intermittent.
Fix
CollisionEventData.None).This restores the invariant VP has: a locked ball carries no hit object, and a ball only collides with what the hit test of the same cycle found.
Diagnostics
Every ball created in a kicker now logs its spawn position and the kicker's local, playfield and world position, and the capture logs where the ball ended up and the capture height the kicker state holds. Each kicker logs its capture height once at initialisation. A kicker rotated by a rotator mech also keeps its height, since the rotated position only carried x and y.
Tests
StaleCollisionEventTests: a new ball has no pending collision; a frozen ball with the default event over a floor is neither moved by the static phase nor bounced by a stale ball-to-ball event. Physics suites pass headlessly (the play-mode fixtures need a keyboard in batch mode and are unaffected).