v1.1: Performance optimizations and bug fixes

Major improvements:
- NumPy-based collision system supporting 200+ units (~3ms/frame)
- Spatial hashing with vectorized distance calculations
- 4-pass game loop ensuring correct collision timing
- Blood overlay system with pre-generated stain pool
- Cached render positions and viewport bounds
- Spawn protection preventing rats spawning on weapons

Bug fixes:
- Fixed bombs not killing rats (collision system timing)
- Fixed gas not affecting rats (collision system timing)
- Fixed rats spawning on weapons (added has_weapon_at check)
- Fixed AttributeError with Gas collisions (added isinstance check)
- Fixed blood stain transparency (RGBA + SDL_BLENDMODE_BLEND)
- Reduced point lifetime from 200 to 90 frames (~1.5s)
- Blood layer now clears on game restart

Technical changes:
- Added engine/collision_system.py with CollisionLayer enum
- Updated all units to use collision layers
- Pre-allocate NumPy arrays with capacity management
- Hybrid collision approach (<10 simple, ≥10 vectorized)
- Python 3.13 compatibility
This commit is contained in:
2025-10-24 20:04:26 +02:00
parent 12836dd2d2
commit b4224ed3a1
12 changed files with 619 additions and 135 deletions
+39 -9
View File
@@ -7,7 +7,7 @@ import json
from engine import maze, sdl2 as engine, controls, graphics, unit_manager, scoring
from engine.collision_system import CollisionSystem
from units import points
from engine.user_profile_integration import UserProfileIntegration, get_global_leaderboard
from engine.user_profile_integration import UserProfileIntegration
class MiceMaze(
@@ -103,6 +103,10 @@ class MiceMaze(
}
self.blood_stains = {}
self.background_texture = None
# Clear blood layer on game start/restart
self.blood_layer_sprites.clear()
for _ in range(5):
self.spawn_rat()
@@ -167,12 +171,16 @@ class MiceMaze(
self.unit_positions.clear()
self.unit_positions_before.clear()
# First pass: move all units and update their positions
for unit in self.units.copy().values():
unit.move()
# Second pass: register all units in collision system and draw
# First pass: Register all units in collision system BEFORE move
# This allows bombs/gas to find victims during their move()
for unit in self.units.values():
# Calculate bbox if not yet set (first frame)
if not hasattr(unit, 'bbox') or unit.bbox == (0, 0, 0, 0):
# Temporary bbox based on position
x_pos = unit.position[0] * self.cell_size
y_pos = unit.position[1] * self.cell_size
unit.bbox = (x_pos, y_pos, x_pos + self.cell_size, y_pos + self.cell_size)
# Register unit in optimized collision system
self.collision_system.register_unit(
unit.id,
@@ -182,11 +190,33 @@ class MiceMaze(
unit.collision_layer
)
# Maintain backward compatibility dictionaries (can be removed later)
# Maintain backward compatibility dictionaries
self.unit_positions.setdefault(unit.position, []).append(unit)
self.unit_positions_before.setdefault(unit.position_before, []).append(unit)
# Third pass: check collisions and draw
# Second pass: move all units (can now access collision system)
for unit in self.units.copy().values():
unit.move()
# Third pass: Update collision system with new positions after move
self.collision_system.clear()
self.unit_positions.clear()
self.unit_positions_before.clear()
for unit in self.units.values():
# Register with updated positions/bbox from move()
self.collision_system.register_unit(
unit.id,
unit.bbox,
unit.position,
unit.position_before,
unit.collision_layer
)
self.unit_positions.setdefault(unit.position, []).append(unit)
self.unit_positions_before.setdefault(unit.position_before, []).append(unit)
# Fourth pass: check collisions and draw
for unit in self.units.copy().values():
unit.collisions()
unit.draw()
@@ -205,7 +235,7 @@ class MiceMaze(
def game_over(self):
if self.game_end[0]:
if not self.combined_scores:
self.combined_scores = get_global_leaderboard(4)
self.combined_scores = self.profile_integration.get_global_leaderboard(4)
global_scores = []
for entry in self.combined_scores: