Crowd-Control: Building a Physics-Driven Arena Combat Game

Inspiration

Crowd-Control started with a simple question: "Can I build a fast-paced game in Python that actually feels good to play?"

I was inspired by:

  • Game feel research from studios like Vlambeer - how small details like screen shake and particles make games satisfying
  • Physics-based games like Rocket League where simple rules create complex, fun moments
  • Modern game engines like Unity and how they organize code into clean systems
  • A personal challenge to prove Python isn't too slow for real-time games

This project became my answer - a multiplayer arena brawler where players dash and collide in a shrinking circle, combining physics, AI, and polish.


What I Learned

1. Vector Math Makes Game Physics Work

Every movement and collision uses vector mathematics. Here's what I implemented:

Finding direction (makes movement consistent):

$$\text{Direction} = \frac{\text{velocity vector}}{\text{velocity length}}$$

Bouncing off surfaces (realistic collisions):

$$\text{New velocity} = \text{Old velocity} - 2 \times (\text{velocity · normal}) \times \text{normal}$$

Knockback force (stronger hits up close):

$$\text{Force} = \frac{\text{strength}}{\text{distance}^2}$$

These formulas aren't just theory - they're what makes collisions feel fair and predictable.


2. Smart Collision Detection Saves Performance

The Problem: Checking every player against every other player is slow. With 8 players, that's 64 checks per frame!

The Solution: I divided the game arena into a grid. Now players only check collisions with others in nearby grid squares.

  • Before: Checking everyone against everyone = slow
  • After: Only checking nearby players = 4x faster

Result: Smooth 60 FPS even with 8 players and hundreds of particle effects!


3. Making AI That Feels Smart

I didn't want random-moving bots. I wanted AI that makes decisions. Each AI evaluates:

  • Distance to nearest opponent (closer = more dangerous)
  • Direction opponent is facing (are they looking at me?)
  • Dash availability (can I escape if needed?)

The AI combines these factors into a "threat score" that decides whether to attack, retreat, or position strategically.

Result: AI that kites when weak, attacks when strong, and avoids arena edges - it feels alive!


4. "Game Feel" is About Details

My physics were mathematically perfect, but the game felt empty. Then I added:

  • Hitstop: Everything freezes for a split second on collision (makes hits feel powerful)
  • Screen shake: Camera jiggles based on impact strength
  • Particles: Burst in the direction of collision
  • Impact rings: Expanding circles that fade away
  • Color distortion: Slight RGB split during intense moments

These tiny additions transformed the game from "it works" to "this feels amazing!"


5. The Shrinking Arena Creates Tension

The arena gets smaller over time using this formula:

$$\text{Arena radius} = \text{Starting radius} \times e^{-0.002 \times \text{time}}$$

Translation: The arena shrinks slowly at first, then faster. This creates natural game phases:

  • Early game: Lots of space, positioning matters
  • Late game: Cramped quarters, aggressive play required

Keeping physics, AI, and visuals all synchronized with this changing boundary taught me how to manage complex, interconnected systems.


How I Built It

System Architecture

I organized the code into independent modules:

Game Core
├── Scene Manager (handles menus, gameplay, results screen)
├── Physics Engine (movement, collisions, forces)
├── Spatial Grid (makes collision detection fast)
├── Particle System (visual effects)
├── Power-Up System (spawns items, applies effects)
├── AI Manager (controls all bot players)
└── Replay Recorder (saves matches for review)

Each system does one job well and doesn't interfere with others.


Technology Used

  • Pygame: Graphics, input, and sound
  • NumPy: Fast math for physics calculations
  • Python: All game logic (proving Python can handle real-time games!)
  • JSON: Saving and loading replay files

Development Process

** Core Gameplay**

  • Player movement with keyboard controls
  • Basic collision detection between circles
  • Dash mechanic with cooldown timer

** Performance**

  • Found performance bottleneck using profiler tools
  • Added grid system for collision detection
  • Created particle pool (reuse particles instead of creating new ones)

** Polish**

  • Built particle effects system
  • Added screen shake and hitstop
  • Tuned knockback forces until collisions felt satisfying

** Intelligence**

  • Programmed AI decision-making
  • Added power-ups with visual feedback
  • Built replay system to watch matches back
  • Final tweaking of speeds, cooldowns, and arena shrink rate

Challenges I Faced

Challenge 1: Game Slowed Down With Multiple Players

The Problem: When 4+ players collided at once, the game dropped from 60 FPS to 15 FPS. Unplayable!

Finding the Cause: I used Python's profiler and found:

  • 68% of processing time was checking collisions
  • I was checking every player against every other player (very slow!)
  • Creating new particle objects every frame caused lag spikes

The Fix:

  1. Grid System: Divided arena into squares - only check collisions with nearby players
  2. Particle Recycling: Create 200 particles at game start, reuse them instead of making new ones

Result: Smooth 60 FPS with 8 players and 500+ particles!


Challenge 2: Screen Effects Felt "Off"

The Problem: Screen shake and freeze-frame effects didn't sync perfectly with collisions - they felt delayed or disconnected.

The Cause: Visual effects were using real time, but physics used game time. They drifted apart.

The Fix: Created one master clock that both systems use. When I freeze the game for hitstop, everything stops together. When I unfreeze, everything resumes in sync.

Result: Effects feel immediate and connected to gameplay!


Challenge 3: AI Couldn't Handle Shrinking Arena

The Problem: AI players fought near the arena edge, got pushed out, and died instantly. They didn't "understand" the boundary was moving.

The Cause: AI only looked at current arena size, not future size.

The Fix: AI now predicts where the boundary will be in 3 seconds and avoids those areas:

Future arena size = Current size × e^(-shrink rate × 3 seconds)
Safe zone = Future size - safety margin

AI prioritizes survival over fighting when near predicted danger zones.

Result: AI win rate increased from 12% to 48%!


Challenge 4: Rare "Phasing Through" Bug

The Problem: Very rarely, players would pass through each other instead of colliding. Couldn't reproduce it consistently.

Finding the Pattern: After many test runs, I noticed it only happened when:

  • Players moved very slowly
  • They hit each other at shallow angles (grazing)

The Cause: When numbers get very small (like 0.0001), computers make tiny rounding errors. My collision math broke down with these tiny velocities.

The Fix:

  1. If player velocity is below 0.1, just set it to zero
  2. Add a small buffer (0.1 pixels) to collision detection
  3. Built a replay system to capture and rewatch bugs

Result: Bug completely eliminated!


Impact and What I Gained

Technical Skills

This project changed how I think about programming:

  • Performance matters: Beautiful code means nothing if it's slow
  • Math is practical: Physics formulas directly control how fun the game feels
  • Details create quality: 10% more polish creates 90% better experience

Proving Python's Capabilities

Crowd-Control shows that Python can build real-time games when you:

  • Use smart algorithms (grid system)
  • Optimize critical paths (particle pooling)
  • Profile and measure (find bottlenecks, fix them)

Personal Growth

This hackathon reinforced a key lesson:

Curiosity asks "what if?" Consistency builds the answer. Craftsmanship makes it worth building.


Try It Yourself

Clone the repository, press SPACE to start, and experience what happens when physics, AI, and game feel come together. Every system is documented - fork it, learn from it, build on it!

The code is modular and commented because I built this not just to win a hackathon, but to share knowledge with the community.

Built With

+ 15 more
Share this project:

Updates