Game Dev
Design
Balance
JavaScript
Boss Design

Balancing Difficulty and Boss Encounters

November 3, 2025

Cover image showing a balanced scale with a game controller

Balance emerged from many small knobs exposed via gameSettings: move speeds, attack intervals, ranges, and damage values. We iterated to make behaviors legible and punishable without feeling random.

Telegraphs first: Melee units like WeakNinja and NormalNinja use an attack cone with range checks; NormalNinja adds a visible spin-up (attackDuration with chargeDelayTimer) before committing to a fast charge. While spinning, it’s invulnerable to prevent trading damage and to reward timing.

Spacing games: RangeNinja maintains a preferred distance and fires a 3-way spread. We tuned its preferredDistance, projectile speed, and spawn interval to keep pressure without creating bullet hell.

Boss fairness: EliteNinja’s state machine alternates pressure patterns—MultiDash (short, bursty commits), Shuriken Rain (zoning), Spin Attack (pursuit), and Teleport (reposition). To avoid cheese and stun-locks, we added a damage rate limiter: at most two valid hits per second via damageCooldownActive. Hits require the player to be spinning (player.isBoosting), making windows explicit.

UX glue: Boss tracking (currentBoss, bossActive) feeds the HP bar; spawner-tagged enemies (isSpawnerEnemy) don’t yield EXP; effects route through onEnemySpawn() for consistent audio. Collision resolution uses circle-vs-rect tests and gentle penetration pushes to prevent sticky walls.

Our north star: clean cause-and-effect. If you dodge the telegraph, you avoid damage. If you meet the window with a spin, you’re rewarded. The numbers are tuned, but the readability is what makes the system sing.