From 12097db2443bc8956f8444b1875d8da893e1dec8 Mon Sep 17 00:00:00 2001 From: Paul V Craven Date: Thu, 1 Oct 2026 09:56:24 -0500 Subject: [PATCH] Use cached hit box bounds in the spatial hash SpatialHash._get_cell_bounds() scanned the hit box's adjusted points itself. It now reads HitBox.get_adjusted_bounds(), which caches the bounds until the points change and shares them with collision checks. For sprites that haven't moved since their bounds were last needed, querying is ~40% faster and add + remove ~33% faster. Moving sprites are unchanged, since their points are recalculated either way. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 1 + arcade/sprite_list/spatial_hash.py | 9 ++++----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 009a518d6..8b2d37261 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ Arcade [PyPi Release History](https://pypi.org/project/arcade/#history) page. ### Misc Changes - Sped up sprite collision checks. Sprites that pass the quick distance check are now compared by cached hit box bounds before the polygon test, and the polygon test skips horizontal and vertical edges, which the bounds check already covers. Checks that reach the polygon test are about 2-4x faster, e.g. 8.0 to 2.3 µs for two box hit boxes and 21.6 to 10.2 µs for two default octagon hit boxes. `are_polygons_intersecting` is also faster (7.3 to 1.6 µs for two rectangles). - Sped up collision checks further by caching each hit box's distinct edge directions. Parallel edges (such as opposite sides of the default octagon hit boxes, or matching edges on two sprites with the same angle) are only tested once, and the cache is kept when a sprite moves. Two unrotated octagon hit boxes go from 12.0 to 4.6 µs, and two rotated 30° from 20.8 to 6.9 µs. +- `SpatialHash` now uses the hit box's cached bounds to find a sprite's grid cells, so they're shared with collision checks. Adding, removing, and querying for sprites that haven't moved is about 33-40% faster. - Added collision benchmark scripts in `benchmarks/collisions/` (`micro.py`, `hit_box.py`, and `compare_reference.py`, which checks collision results against a simple reference on random sprite pairs) and `benchmarks/spatial_hash/queries.py`. - Updated the optional `pymunk` extra to 7.3.0 (from 7.2.0). Packaging-only release (free-threaded CPython and pyodide wheels, improved type hints) with no breaking changes or deprecations. diff --git a/arcade/sprite_list/spatial_hash.py b/arcade/sprite_list/spatial_hash.py index f0878b7df..f265f798a 100644 --- a/arcade/sprite_list/spatial_hash.py +++ b/arcade/sprite_list/spatial_hash.py @@ -110,11 +110,10 @@ def reset(self): def _get_cell_bounds(self, sprite: BasicSprite) -> tuple[IPoint, IPoint]: """Get the min and max cells covered by a sprite's hit box.""" - # One pass over the points instead of using left/right/bottom/top, - # which each fetch and scan the hit box points separately. - x_points, y_points = zip(*sprite.hit_box.get_adjusted_points()) - min_point = self.hash((trunc(min(x_points)), trunc(min(y_points)))) - max_point = self.hash((trunc(max(x_points)), trunc(max(y_points)))) + # The hit box caches its bounds, so collision checks can reuse them + left, right, bottom, top = sprite.hit_box.get_adjusted_bounds() + min_point = self.hash((trunc(left), trunc(bottom))) + max_point = self.hash((trunc(right), trunc(top))) return min_point, max_point def add(self, sprite: SpriteType) -> None: