TECH DETAILS

Tower Defense - Day 12

Strategy Game

Table of Contents

01 / Tech Stack

A complete tower defense game built with vanilla JavaScript and Canvas - featuring pathfinding, multiple tower types, projectile systems, and wave management.

// Core game constants const TILE_SIZE = 40; const GRID_WIDTH = 16; const GRID_HEIGHT = 12; // Tower type definitions const TOWER_TYPES = { arrow: { cost: 50, damage: 15, range: 100, fireRate: 30 }, cannon: { cost: 100, damage: 50, range: 80, splash: 30 }, ice: { cost: 75, damage: 8, range: 90, slow: 0.5 }, lightning: { cost: 150, damage: 35, range: 120, chain: 3 } };
Design Philosophy

Each tower type has a unique mechanic: Arrow for DPS, Cannon for splash, Ice for crowd control, Lightning for chain damage. This creates strategic depth.

02 / Pathfinding & Map System

The map uses a 2D array where different values represent different tile types. Enemies follow a pre-calculated path from start to end.

Map Representation

// Map tiles: 0=grass, 1=path, 2=start, 3=end const MAP = [ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [2,1,1,1,1,0,0,0,0,0,1,1,1,1,1,0], // ... (snaking path creates longer enemy travel time) [0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,3] ];

Path Calculation (Flood Fill)

function calculatePath() { PATH = []; let visited = new Set(); let current = null; // Find start tile (value 2) for (let y = 0; y < GRID_HEIGHT; y++) { for (let x = 0; x < GRID_WIDTH; x++) { if (MAP[y][x] === 2) { current = { x, y }; PATH.push({ x: x * TILE_SIZE + TILE_SIZE/2, y: y * TILE_SIZE + TILE_SIZE/2 }); visited.add(`${x},${y}`); break; } } } // Follow path using 4-directional flood fill const directions = [[1,0], [-1,0], [0,1], [0,-1]]; while (current) { let found = false; for (let [dx, dy] of directions) { let nx = current.x + dx; let ny = current.y + dy; let key = `${nx},${ny}`; if (isValidTile(nx, ny) && !visited.has(key) && (MAP[ny][nx] === 1 || MAP[ny][nx] === 3)) { visited.add(key); current = { x: nx, y: ny }; PATH.push({ x: nx * TILE_SIZE + TILE_SIZE/2, y: ny * TILE_SIZE + TILE_SIZE/2 }); found = true; break; } } if (!found) break; } }
Find Start (2) -> Check Neighbors -> Follow Path (1) -> Reach End (3)

03 / Tower Targeting System

Each tower scans for enemies within range and targets the closest one. The tower rotates to face its target before firing.

Finding Closest Enemy

class Tower { update() { if (this.cooldown > 0) { this.cooldown--; return; } // Find closest enemy in range let closest = null; let closestDist = Infinity; for (let enemy of enemies) { const dx = enemy.x - this.x; const dy = enemy.y - this.y; const dist = Math.sqrt(dx * dx + dy * dy); if (dist < this.config.range && dist < closestDist) { closest = enemy; closestDist = dist; } } if (closest) { // Rotate towards target this.angle = Math.atan2( closest.y - this.y, closest.x - this.x ); this.shoot(closest); } } }
Targeting Strategies

This uses "closest first" targeting. Other strategies include: "first" (furthest along path), "strongest" (highest HP), or "weakest" (lowest HP).

04 / Projectile & Damage Systems

Projectiles are homing - they track their target's position and deal damage on impact. Different tower types have unique damage effects.

Homing Projectile Movement

function updateProjectiles() { for (let i = projectiles.length - 1; i >= 0; i--) { const p = projectiles[i]; // Update target position for homing if (p.target && enemies.includes(p.target)) { p.targetX = p.target.x; p.targetY = p.target.y; } // Move towards target const dx = p.targetX - p.x; const dy = p.targetY - p.y; const dist = Math.sqrt(dx * dx + dy * dy); if (dist < p.speed) { // Hit! Apply damage based on tower type handleProjectileHit(p); projectiles.splice(i, 1); } else { p.x += (dx / dist) * p.speed; p.y += (dy / dist) * p.speed; } } }

Special Damage Types

function handleProjectileHit(p) { if (p.splash) { // Cannon: Splash damage in radius for (let enemy of enemies) { const dist = distance(enemy, p); if (dist < p.splash) { enemy.takeDamage(p.damage); } } } if (p.slow) { // Ice: Apply slow debuff p.target.takeDamage(p.damage, p.slow); // slow = 0.5 means 50% speed reduction } if (p.chain) { // Lightning: Chain to nearby enemies let nextTarget = findNearestUnhit(p, 80); if (nextTarget && p.chainHit.length < p.chain) { p.chainHit.push(nextTarget); createChainProjectile(p, nextTarget); } } }
Chain Lightning Recursion

Chain lightning creates new projectiles for each jump. Use a chainHit array to prevent hitting the same enemy twice and avoid infinite loops.

05 / Wave Management

Each wave spawns enemies at intervals with increasing difficulty. Higher waves introduce new enemy types and more enemies.

Enemy Type Scaling

function getEnemyTypes(waveNum) { const baseHealth = 30 + waveNum * 15; const baseSpeed = 1 + waveNum * 0.1; const types = [ // Normal - always available { health: baseHealth, speed: baseSpeed, color: '#ff6b6b', radius: 10, reward: 10 } ]; if (waveNum >= 3) { // Fast - low HP, high speed types.push({ health: baseHealth * 0.6, speed: baseSpeed * 1.5, color: '#ffa500', radius: 8, reward: 15 }); } if (waveNum >= 5) { // Tank - high HP, slow types.push({ health: baseHealth * 2, speed: baseSpeed * 0.6, color: '#8b4513', radius: 14, reward: 25 }); } return types; }

Spawn Timer System

let spawnTimer = 0; let enemiesToSpawn = 0; function updateSpawning() { if (enemiesToSpawn > 0) { spawnTimer++; if (spawnTimer >= 40) { // Spawn every 40 frames const type = currentEnemyTypes[ Math.floor(Math.random() * currentEnemyTypes.length) ]; enemies.push(new Enemy(type)); enemiesToSpawn--; spawnTimer = 0; } } else if (enemies.length === 0 && waveInProgress) { // Wave complete! waveInProgress = false; wave++; gold += 25 + wave * 5; // Wave completion bonus } }
Start Wave -> Spawn Enemies -> All Spawned? -> All Dead? -> Next Wave

06 / Tower Upgrades & Economy

Clicking an existing tower upgrades it, increasing damage and fire rate. The economy balances tower costs with enemy rewards.

Upgrade System

canvas.addEventListener('click', (e) => { const gridX = Math.floor(x / TILE_SIZE); const gridY = Math.floor(y / TILE_SIZE); // Check for existing tower const existingTower = towers.find(t => Math.floor(t.x / TILE_SIZE) === gridX && Math.floor(t.y / TILE_SIZE) === gridY ); if (existingTower) { // Upgrade cost scales with level const upgradeCost = existingTower.config.cost * existingTower.level; if (gold >= upgradeCost && existingTower.level < 3) { gold -= upgradeCost; existingTower.level++; // Visual feedback addParticles(existingTower.x, existingTower.y, '#ffd700', 20); } return; } // Otherwise, place new tower on grass if (selectedTower && MAP[gridY][gridX] === 0) { if (gold >= TOWER_TYPES[selectedTower].cost) { gold -= TOWER_TYPES[selectedTower].cost; towers.push(new Tower(gridX, gridY, selectedTower)); } } });

Upgrade Effects

class Tower { shoot(target) { projectiles.push({ damage: this.config.damage * this.level, // +100% per level // ... other properties }); // Fire rate also improves this.cooldown = Math.floor( this.config.fireRate / this.level ); } draw() { // Show stars for upgraded towers if (this.level > 1) { ctx.fillStyle = '#ffd700'; ctx.font = 'bold 10px JetBrains Mono'; ctx.fillText( '★'.repeat(this.level - 1), this.x, this.y - 18 ); } } }
Economy Balance

Upgrade cost = base cost × current level. Level 2 costs 1x base, Level 3 costs 2x base. This makes upgrades progressively more expensive but worthwhile for strong positions.