Add get_collision_info for push-out direction and depth - #2910
Merged
Merged
Conversation
get_collision_info(sprite1, sprite2) returns a CollisionInfo with the smallest move that separates two colliding sprites: a unit normal (the direction to move sprite1) and a depth in pixels. It returns None exactly when check_for_collision returns False. It uses the same distance, bounding box, and separating axis stages as check_for_collision, keeping the overlap on each axis instead of only checking for separation. The bounding boxes give the x and y overlaps; the cached edge normals give the rest, divided by their length to get pixels. Ties prefer the y axis, then x, then other edges, and up or right over down or left. Correct for convex hit boxes. check_for_collision is unchanged. get_collision_info costs about 1.2-1.4x as much for overlapping sprites and the same otherwise. Add it to benchmarks/collisions/micro.py. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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
Adds
arcade.get_collision_info(sprite1, sprite2), which returns how two colliding sprites overlap: the smallest move that separates them (the "minimum translation vector").CollisionInfois aNamedTuple:normal: a unitpyglet.math.Vec2pointing the way to movesprite1. Swapping the sprites flips it.depth: how far to move, in pixels. Always > 0.player.position += info.normal * info.depthworks even though sprite positions are plain tuples:Vec2subclassestuple, so Python usesVec2.__radd__.This is PR 1 of the plan discussed for "push-out direction and depth." A list version and optional use in the physics engines would come later, as separate PRs.
How it works
It uses the same stages as
check_for_collision:It returns the smallest overlap. Because the cached normals aren't unit length (that keeps exact touches exact), each overlap is divided by the normal's length to get pixels.
check_for_collisionis untouched.get_collision_infomakes the same separation comparisons on the same axes, so it returnsNoneexactly whencheck_for_collisionreturnsFalse, including that touching isn't colliding.Behavior notes, all documented
sprite1moving up.normal * depththe hit boxes touch, but floating point rounding can leave them overlapping by a tiny amount. The docstring suggests adding a small extra distance if that matters.Tests
test_get_collision_info_basics: exact x overlap and normal, swapped sprites flipping the normal, sinking into a floor (pushed up exactly 7 px and no longer colliding afterwards), identical sprites in the same place (pushed up), and touching or far apart (None).test_get_collision_info_diagonal: a 45° box gives a diagonal unit normal.test_get_collision_info_matches_check_for_collision: 3,000 random pairs, including concave detailed hit boxes, in both argument orders.Noneexactly whencheck_for_collisionisFalse.test_get_collision_info_separates: 3,000 random convex pairs (rotated, flipped, scaled). For each collision:depthmatches a brute-force minimum over every edge normal plus x/y,depth + 1e-6separates the sprites, and movingdepth - 1e-6doesn't.SpriteListpassed as the second argument.check_for_collision, and among 25,136 convex collisions no depth mismatches and no failed separations.sin(π/4)value simulated, so the results don't depend on the platform. Full suite on pyglet 3.0.dev11: 1424 passed. The 3 failures are the render tests that only fail on my machine.Performance
benchmarks/collisions/micro.pynow timesget_collision_infoon the same pairs ascheck_for_collision. µs per call, from a clean run:check_for_collisionget_collision_infoSo it's about 1.2–1.4× the cost of a yes/no check when sprites overlap, and the same otherwise.
🤖 Generated with Claude Code