diff options
Diffstat (limited to 'html/tower/js/game.js')
-rw-r--r-- | html/tower/js/game.js | 88 |
1 files changed, 69 insertions, 19 deletions
diff --git a/html/tower/js/game.js b/html/tower/js/game.js index 1099c46..0e79e4e 100644 --- a/html/tower/js/game.js +++ b/html/tower/js/game.js @@ -113,53 +113,103 @@ function spawnEnemies(timestamp) { } /** - * Renders all game elements to the canvas + * Renders all game elements to the canvas using a layered approach. + * This function demonstrates several key game development patterns: * - * Key concepts: - * - Layer-based rendering - * - Canvas drawing order (background to foreground) - * - Separation of rendering and game logic + * 1. Canvas State Management: + * - Uses save()/restore() to isolate rendering contexts + * - Resets transform matrix to prevent state leaks + * - Maintains clean state between rendering phases + * + * 2. Layered Rendering Pattern: + * - Renders in specific order (background → entities → UI) + * - Each layer builds on top of previous layers + * - Separates rendering concerns for easier maintenance + * + * 3. Separation of Concerns: + * - Each render function handles one specific type of game element + * - UI rendering is isolated from game element rendering + * - Clear boundaries between different rendering responsibilities + * + * The rendering order is important: + * 1. Grid (background) + * 2. Particles (effects under entities) + * 3. Projectiles (dynamic game elements) + * 4. Towers (static game entities) + * 5. Enemies (moving game entities) + * 6. UI (top layer) */ function renderGame() { - // Clear and reset canvas state at the start + // Reset the canvas transform matrix to identity + // This prevents any previous transformations from affecting new renders ctx.setTransform(1, 0, 0, 1, 0, 0); + + // Clear the entire canvas to prevent ghosting + // This is crucial for animation smoothness ctx.clearRect(0, 0, canvas.width, canvas.height); - // Save initial state + // Save the initial clean state + // This is part of the state stack pattern used in canvas rendering ctx.save(); - // Render game elements - renderGrid(ctx, gameState.grid); - renderParticles(ctx, gameState.particles); - renderProjectiles(ctx, gameState.projectiles); - renderTowers(ctx, gameState.towers); - renderEnemies(ctx, gameState.enemies); + // Render game world elements in specific order + // This creates the layered effect common in 2D games + renderGrid(ctx, gameState.grid); // Background layer + renderParticles(ctx, gameState.particles); // Effect layer + renderProjectiles(ctx, gameState.projectiles); // Dynamic elements + renderTowers(ctx, gameState.towers); // Static entities + renderEnemies(ctx, gameState.enemies); // Moving entities - // Restore to clean state before UI + // Restore to clean state before UI rendering + // This ensures UI rendering isn't affected by game world rendering ctx.restore(); ctx.save(); - // Render UI with clean state + // Render UI elements last so they appear on top + // UI is rendered with its own clean state to prevent interference renderUI(ctx, gameState); - // Final restore + // Final state restoration + // Ensures clean state for next frame ctx.restore(); } -// Start the game +/** + * Initializes the game by: + * 1. Generating the path for enemies to follow + * 2. Setting up initial enemy count + * 3. Binding event listeners + * 4. Starting the game loop + * + * Uses Promise-based path generation to handle async initialization + */ generatePath(gameState.grid).then(path => { gameState.path = path; - enemiesRemaining = Math.floor(Math.random() * 26) + 5; // 5-30 enemies + // Random enemy count between 5-30 for variety + enemiesRemaining = Math.floor(Math.random() * 26) + 5; initializeEventListeners(); + // Start the game loop using requestAnimationFrame for smooth animation requestAnimationFrame(gameLoop); }); +/** + * Transitions the game from placement to combat phase. + * Demonstrates state machine pattern commonly used in games. + * + * Side effects: + * - Updates game phase + * - Disables UI elements + * - Updates visual feedback + */ function startCombat() { if (gameState.phase === GamePhase.PLACEMENT && gameState.towers.length > 0) { + // State transition gameState.phase = GamePhase.COMBAT; + + // UI updates document.getElementById('startCombat').disabled = true; - // Disable tower palette + // Visual feedback for disabled state document.querySelectorAll('.tower-option').forEach(option => { option.draggable = false; option.style.cursor = 'not-allowed'; |