From ef353a5a1f2f6c18cf58cb8d86dfcbd58bf5027a Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Wed, 5 Aug 2026 18:32:50 +0200 Subject: [PATCH] perf: Insertion-sort the sweep broadphase and swap-remove its active list --- .../collisions/broadphase/sweep/sweep.dart | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/flame/lib/src/collisions/broadphase/sweep/sweep.dart b/packages/flame/lib/src/collisions/broadphase/sweep/sweep.dart index f79cc6e33e0..e446d5ce2b3 100644 --- a/packages/flame/lib/src/collisions/broadphase/sweep/sweep.dart +++ b/packages/flame/lib/src/collisions/broadphase/sweep/sweep.dart @@ -17,7 +17,21 @@ class Sweep> extends Broadphase { @override void update() { - items.sort((a, b) => a.aabb.min.x.compareTo(b.aabb.min.x)); + // Between two ticks the hitboxes only move a little, so [items] is + // always nearly sorted: an insertion sort runs in close to linear time + // here, where a general-purpose sort would pay its full O(n log n) on + // every tick. This also avoids allocating a comparator closure per tick. + final items = this.items; + for (var i = 1; i < items.length; i++) { + final item = items[i]; + final minX = item.aabb.min.x; + var previousIndex = i - 1; + while (previousIndex >= 0 && items[previousIndex].aabb.min.x > minX) { + items[previousIndex + 1] = items[previousIndex]; + previousIndex--; + } + items[previousIndex + 1] = item; + } } @override @@ -44,7 +58,10 @@ class Sweep> extends Broadphase { _prospectPool.acquire(item, activeItem); } } else { - _active.remove(activeItem); + // The order of the active list does not matter, so the removal can + // swap in the last element instead of searching and shifting. + _active[i] = _active.last; + _active.removeLast(); } } _active.add(item);