📝

Documentation and Debugging

This page explains how my Peppa Pig Fighting Game uses code documentation, JSDoc comments, console logs, DevTools, hitbox visualization, network debugging, application storage checks, and testing to make the project easier to understand and fix.

Learning Objective Project Evidence Required Assessment Method
Code Documentation Section 1 explains comments and JSDoc for classes, constructors, parameters, return values, and important logic. Review code comments, method headers, and portfolio explanations.
Console Debugging Section 2 explains console.log(), console.warn(), and console.error() for tracking game state. Demo console output while testing the game.
Hitbox Debugging Section 3 explains visible hitbox rectangles for verifying collision boundaries. Compare visual sprite location to collision rectangle.
Network Debugging Section 4 explains inspecting API requests, status codes, and JSON payloads in the Network tab. Review fetch calls and response handling.
Application Debugging Section 5 explains checking localStorage values for player name, game mode, selected characters, and leaderboard data. Inspect Application tab storage values after gameplay.
Testing and Verification Section 6 explains testing player attacks, boss damage, win/loss conditions, leaderboard behavior, and error handling. Live demo and test explanation.

Specific Documentation and Debugging Requirements


1. Code Documentation

Requirement: Use comments and JSDoc to explain classes, constructors, parameters, return values, and important logic

Code documentation explains what the program does before someone reads every line of code. In my project, documentation is used to explain the boss class, constructor parameters, method behavior, return values, and important game logic. This makes the game easier to review because the reader can understand the purpose of each class and method.

/**
 * PeppaBossEnemy — custom boss class extending the Enemy base.
 * Adds health system, defeat state, movement AI, and hitbox debugging.
 *
 * @class PeppaBossEnemy
 * @extends Enemy
 */
class PeppaBossEnemy extends Enemy {
  /**
   * @param {Object} data - Sprite and config data for this enemy.
   * @param {number} data.health - Starting HP for this boss.
   * @param {number} data.moveSpeed - Pixels per frame toward the player.
   * @param {Object} gameEnv - Shared game environment reference.
   */
  constructor(data = null, gameEnv = null) {
    super(data, gameEnv);
    this.health = data?.health ?? 3;
    this.maxHealth = this.health;
    this.moveSpeed = data?.moveSpeed ?? 0.8;
    this.isDefeated = false;
  }
}
/**
 * Applies damage to the boss.
 *
 * @param {number} amount - HP to subtract from the boss.
 * @returns {boolean} true if this hit defeated the boss.
 */
takeDamage(amount = 1) {
    if (this.isDefeated) return true;

    this.health = Math.max(0, this.health - amount);
    return this.health === 0;
}

This proves code documentation because the project uses comments and JSDoc to explain classes, parameters, return values, and the purpose of important gameplay methods.

2. Console Debugging

Requirement: Use console.log(), console.warn(), and console.error() to track game state

Console debugging helps check what the game is doing while it runs. My project uses console output to track boss spawning, HP changes, laser hits, defeated state, hitbox positions, leaderboard saving, and API errors. This helps find bugs without needing to guess what happened.

console.log(`[PeppaBossEnemy] Spawned: ${data?.id} with ${this.health} HP`);
console.log(`[Boss] Hit! HP: ${this.health}/${this.maxHealth}`);
console.log(`[Boss] Defeated!`);
console.warn(`[Boss] Invulnerable, ignoring damage.`);
console.log(`[Hitbox] pos=(${Math.round(this.position.x)},${Math.round(this.position.y)})`);
console.log(`[Leaderboard] Score saved:`, data);
console.error(`[Leaderboard] POST failed:`, error.message);

This proves console debugging because the project uses console messages to inspect game state, confirm behavior, and identify errors during testing.

3. Hitbox Debugging

Requirement: Use a visible hitbox rectangle to verify collision boundaries

Hitbox debugging makes collision detection visible. In the game, the player sees sprite images, but the collision system checks rectangles. Drawing a visible rectangle over the boss helps verify whether the collision area matches the visible character.

/**
 * Draws a red rectangle over the boss sprite for collision debugging.
 */
drawHitbox() {
    const ctx = this.gameEnv?.ctx;
    if (!ctx) return;

    ctx.save();
    ctx.strokeStyle = 'rgba(255, 0, 0, 0.85)';
    ctx.lineWidth = 2;
    ctx.strokeRect(this.position.x, this.position.y, this.width, this.height);
    ctx.fillStyle = 'rgba(255, 0, 0, 0.08)';
    ctx.fillRect(this.position.x, this.position.y, this.width, this.height);
    ctx.restore();
}

This proves hitbox debugging because the project includes a visual debugging tool for checking collision boundaries against the visible sprite.

4. Network Debugging

Requirement: Inspect API requests, status codes, and JSON payloads in the Network tab

Network debugging is used when the game communicates with an API. API requests can fail because of a bad URL, server errors, CORS issues, wrong request data, or connection problems. The Network tab helps verify the request URL, method, status code, request body, and JSON response.

// API error handling pattern
try {
    const response = await fetch(url, fetchOptions);

    if (!response.ok) {
        throw new Error(`HTTP ${response.status}`);
    }

    const data = await response.json();
    return data;
} catch (error) {
    console.error(`[API] Request failed: ${error.message}`);
    return null;
}

This proves network debugging because the project checks API responses, handles failed requests, and uses browser tools to inspect web traffic.

5. Application Debugging

Requirement: Check localStorage values for player name, game mode, characters, and leaderboard data

Application debugging focuses on browser storage. My game uses localStorage to save player names, game mode, selected characters, and leaderboard scores. The Application tab in DevTools lets me check whether the correct keys and values were saved after gameplay.

const savedName = localStorage.getItem('peppaPlayerName');
const savedMode = localStorage.getItem('peppaGameMode');
const savedScores = localStorage.getItem('peppa-leaderboard-lvl1');

console.log('Saved player name:', savedName);
console.log('Saved game mode:', savedMode);
console.log('Saved leaderboard:', savedScores);

This proves application debugging because the project checks stored browser data to verify that reload behavior, character choices, game mode, and leaderboard saving work correctly.

6. Testing and Verification

Requirement: Test player attacks, boss damage, win/loss conditions, leaderboard behavior, and error handling

Testing and verification means checking that each feature works the way it is supposed to work. My project is tested by playing through important game systems and comparing the actual result to the expected result.

// Manual testing checklist example
console.log('TEST: Press Space and confirm Player 1 laser spawns.');
console.log('TEST: Press Enter in two-player mode and confirm Player 2 laser spawns.');
console.log('TEST: Hit boss and confirm boss HP decreases.');
console.log('TEST: Reload page and confirm localStorage values still exist.');
console.log('TEST: Turn off API endpoint and confirm try/catch handles the error.');

This proves testing and verification because the project checks gameplay behavior, saved data, API errors, and user-visible output during a live demo.

📝

JSDoc Comments and Code Documentation


Code documentation makes the project easier to understand because it explains what each class or method does before someone reads the actual code. In my game, documentation is especially important because different systems work together, such as the boss class, laser system, player input, HUD output, localStorage, and leaderboard API.

JSDoc comments are useful because they describe parameters, return values, and class purpose. This helps show that the code is not just copied into the project, but understood well enough to explain clearly.

/**
 * PeppaBossEnemy — custom boss class extending the Enemy base.
 * Adds health system, defeat state, movement AI, and hitbox debugging.
 *
 * @class PeppaBossEnemy
 * @extends Enemy
 */
class PeppaBossEnemy extends Enemy {
  /**
   * @param {Object} data - Sprite and config data for this enemy.
   * @param {number} data.health - Starting HP for this boss.
   * @param {number} data.moveSpeed - Pixels per frame toward the player.
   * @param {Object} gameEnv - Shared game environment reference.
   */
  constructor(data = null, gameEnv = null) {
    super(data, gameEnv);
    this.health = data?.health ?? 3;
    this.maxHealth = this.health;
    this.moveSpeed = data?.moveSpeed ?? 0.8;
    this.isDefeated = false;
  }
}

This documentation explains the purpose of the class and the type of data the constructor expects. The comments make the code easier to review because the reader can quickly understand that this class controls the boss enemy's health, movement, and defeated state.


⚙️

Method Documentation with Parameters and Return Values


Methods that change game state should be documented because they affect gameplay directly. For example, takeDamage(amount) changes boss health and returns whether the boss has been defeated.

/**
 * Applies damage to the boss.
 *
 * @param {number} amount - HP to subtract from the boss.
 * @returns {boolean} true if this hit defeated the boss.
 */
takeDamage(amount = 1) {
    if (this.isDefeated) return true;

    this.health = Math.max(0, this.health - amount);

    if (this.health === 0) {
        this.isDefeated = true;
        this.canvas.style.filter = 'grayscale(1) brightness(0.8)';
        this.canvas.style.opacity = '0.6';
    }

    return this.isDefeated;
}

The @param tag explains what the method receives, and the @returns tag explains what the method sends back. In this case, the return value matters because the level logic can use it to decide whether the battle should end.


🐛

Debugging with DevTools


Debugging was important because the game has several systems running at the same time. Player movement, boss movement, lasers, collisions, score saving, localStorage, and API requests all have to work together. I used browser DevTools to inspect the game's behavior and find problems faster.

DevTools Area What I Used It For
Console Tracked HP changes, laser spawns, boss defeats, score saving, and API errors while the game was running.
Sources Used breakpoints to pause inside important methods like takeDamage() and inspect variables line by line.
Network Checked leaderboard or API fetch() requests, request bodies, response codes, and JSON response data.
Application Checked localStorage keys like peppaGameMode, player names, character choices, and local leaderboard values.
Elements Inspected HUD divs, canvas layers, button elements, and styling to make sure UI output was placed correctly.

💬

Console Logging Pattern


Console logs helped me check what was happening without stopping the game. I used logs for boss spawning, laser hits, HP changes, defeated state, hitbox positions, leaderboard saving, and API errors.

// Strategic console.log placement — tracks state during gameplay
console.log(`[PeppaBossEnemy] Spawned: ${data?.id} with ${this.health} HP`);
console.log(`[Boss] Hit! HP: ${this.health}/${this.maxHealth}`);
console.log(`[Boss] Defeated!`);
console.warn(`[Boss] Invulnerable, ignoring damage.`);
console.log(`[Hitbox] pos=(${Math.round(this.position.x)},${Math.round(this.position.y)})`);
console.log(`[Leaderboard] Score saved:`, data);
console.error(`[Leaderboard] POST failed:`, error.message);

These logs make debugging easier because I can see when a method runs and whether the important variables have the values I expect.


🎯

Hitbox Debugging


Hitbox debugging helped me compare what the player sees to what the collision system actually checks. Sometimes a sprite image can be larger than the visible character, so a laser might look like it missed while the rectangular hitbox still counts it as a hit. Drawing the hitbox makes that problem easier to fix.

/**
 * Draws a red rectangle over the boss sprite for collision debugging.
 */
drawHitbox() {
    const ctx = this.gameEnv?.ctx;
    if (!ctx) return;

    ctx.save();
    ctx.strokeStyle = 'rgba(255, 0, 0, 0.85)';
    ctx.lineWidth = 2;
    ctx.strokeRect(this.position.x, this.position.y, this.width, this.height);
    ctx.fillStyle = 'rgba(255, 0, 0, 0.08)';
    ctx.fillRect(this.position.x, this.position.y, this.width, this.height);
    ctx.restore();
}

Why this matters: If the visible character and the collision rectangle do not match, the game feels unfair. Hitbox debugging helps make collision detection match what the player sees on screen.


🌐

Network and API Debugging


Network debugging is important because API calls can fail for reasons that are not visible on the page. The backend might be offline, the request body might be wrong, the server might return an error status, or CORS could block the request. The Network tab helps check the real request and response.

// API error handling pattern
try {
    const response = await fetch(url, fetchOptions);

    if (!response.ok) {
        throw new Error(`HTTP ${response.status}`);
    }

    const data = await response.json();
    return data;
} catch (error) {
    console.error(`[API] Request failed: ${error.message}`);
    return null;
}

This pattern prevents the game from crashing when an API request fails. Instead of breaking gameplay, the project logs the issue and continues running.


Testing and Verification


I tested the game by playing through important features and checking whether the screen output and saved data matched the expected behavior.

Testing connection: These tests prove that documentation and debugging are not separate from the project. They support the actual development process by helping verify that each game system works during a live demo.

← Back to College Ready Blog