Control Structures: Conditionals, Loops, & Nested Conditions

Requirements

  1. Use loops for game object arrays and animation frames
  2. Implement conditionals for collision detection and state transitions
  3. Use nested conditions for complex game logic

Evidence


Conditionals (if/else)

Ghost.js - Conditional Logic for Following Player

getPlayer() {
    return this.gameEnv?.gameObjects?.find(obj => obj instanceof Player) || null;  // ← Conditional OR
}

followPlayer(player) {
    if (!player) return;  // ← Guard clause: conditional check

    const ghostCenter = this.getCenter();
    const playerCenter = typeof player.getCenter === 'function'  // ← Type check conditional
        ? player.getCenter()
        : { x: player.x || 0, y: player.y || 0 };

    const dx = playerCenter.x - ghostCenter.x;
    const dy = playerCenter.y - ghostCenter.y;
    const distance = Math.hypot(dx, dy);

    if (distance <= this.followStopDistance) {  // ← Conditional: distance check
        this.velocity.x = 0;
        this.velocity.y = 0;
        return;  // ← Early return if condition met
    }

    // Continue following if distance > followStopDistance
    const baseSpeed = player?.xVelocity || (this.gameEnv?.innerWidth || 800) / 2000;
    const speed = Math.max(0.3, baseSpeed * this.followSpeedFactor);
}

How it meets the requirement:

  • Guard Clause: if (!player) return prevents null reference errors
  • Type Checking: Uses ternary conditional to handle different player object types
  • Distance Condition: Stops movement when within followStopDistance

DeathBarrier.js - Nested Conditionals for Collision Detection

update() {
    super.update();

    if (this._hasTriggeredDeath) return;  // ← First condition: prevent duplicate triggers

    const player = this.gameEnv?.gameObjects?.find(obj => obj.constructor?.name === 'Player');
    if (!player || !player.canvas || !this.canvas) return;  // ← Multiple AND conditions

    this.isCollision(player);

    if (this.collisionData?.hit) {  // ← Second level condition: check collision
        // NESTED CONDITIONS - Third level: grace period logic
        const timeSinceLevelStart = new Date() - DeathBarrier.levelStartTime;
        if (timeSinceLevelStart < 500) {  // ← Nested condition: grace period active
            console.log('[MazeDebug] Grace period active:', timeSinceLevelStart, 'ms');
            return;  // Exit without triggering death
        }

        // If grace period passed, trigger death
        this._hasTriggeredDeath = true;
        player.isDead = true;

        try {
            showDeathScreen(player, 'You got lost in the maze.');
        } catch (error) {
            console.error('DeathBarrier failed to show death screen:', error);
            this._hasTriggeredDeath = false;
            player.isDead = false;
        }
    }
}

How it meets the requirement:

  • Multiple AND Conditions: if (!player || !player.canvas || !this.canvas)
  • Nested Conditions: Grace period check inside collision check
  • State Management: Uses boolean flags to track collision state

Ghost.js - Direction Logic with Nested Conditionals

followPlayer(player) {
    // ... distance calculations ...

    const nx = dx / distance;
    const ny = dy / distance;

    this.position.x += nx * speed;
    this.position.y += ny * speed;

    // NESTED CONDITIONALS: determine direction based on x/y distances
    if (Math.abs(dx) >= Math.abs(dy)) {  // ← First condition: horizontal movement greater
        this.direction = dx >= 0 ? 'right' : 'left';  // ← Nested ternary: positive/negative x
    } else {  // ← Else: vertical movement greater
        this.direction = dy >= 0 ? 'down' : 'up';  // ← Nested ternary: positive/negative y
    }
}

How it meets the requirement:

  • Nested Conditions: if/else with ternary operators inside
  • Complex Logic: Compares two values to determine animation direction

Loops (for, forEach, Array methods)

GameLevelOutside.js - Starfield Animation Loop

const starLayers = [
    { count: 140, speed: 0.25, size: [0.6, 1.4], alpha: [0.2, 0.5] },
    { count: 80, speed: 0.6, size: [1.0, 2.2], alpha: [0.4, 0.8] },
    { count: 35, speed: 1.1, size: [1.6, 3.4], alpha: [0.5, 0.9] }
];

// ← Array.map() loop: transform each layer into star state
const starState = starLayers.map(layer => ({
    speed: layer.speed,
    stars: Array.from({ length: layer.count }, () => ({  // ← Array.from() loop
        x: Math.random(),
        y: Math.random(),
        r: layer.size[0] + Math.random() * (layer.size[1] - layer.size[0]),
        a: layer.alpha[0] + Math.random() * (layer.alpha[1] - layer.alpha[0])
    }))
}));

// ← forEach loop: update and draw each star
const drawStars = () => {
    starState.forEach(layer => {  // ← For each layer
        layer.stars.forEach(star => {  // ← For each star in layer (nested loop)
            star.y += layer.speed / h;  // Update position
            if (star.y > 1) {  // Reset if off-screen
                star.y = 0;
                star.x = Math.random();
            }
            starCtx.fillStyle = `rgba(200, 220, 255, ${star.a})`;
            starCtx.beginPath();
            starCtx.arc(star.x * w, star.y * h, star.r, 0, Math.PI * 2);
            starCtx.fill();
        });
    });
};

How it meets the requirement:

  • Array.map(): Transform layer configuration into star state
  • Array.from(): Create array of random stars for each layer
  • forEach() Loop: Iterate through layers and stars for drawing
  • Nested Loops: Multiple levels of iteration for animation

GameLevelMaze.js - Multiple Loops for Barrier Creation

// All death barriers defined in configuration
const dbarrier_1 = { id: 'dbarrier_1', x: 498 / 505 * width, ... };
const dbarrier_2 = { id: 'dbarrier_2', x: 0 / 505 * width, ... };
// ... more barriers ...

// ← Array of objects that will be looped through by game engine
this.classes = [
    { class: GameEnvBackground, data: bgData },
    { class: Player, data: sprite_data_mc },
    { class: Npc, data: mortyData },
    { class: DeathBarrier, data: dbarrier_1 },  // ← Will be instantiated in a loop
    { class: DeathBarrier, data: dbarrier_2 },
    { class: DeathBarrier, data: dbarrier_3 },
    // ... 16 more barriers ...
    { class: DeathBarrier, data: dbarrier_19 }
];

// Game engine internally iterates:
// for (let config of this.classes) {
//     let obj = new config.class(config.data, gameEnv);
// }

How it meets the requirement:

  • Array Structure: Array of configuration objects for game engine to loop through
  • 19 Barrier Instances: Demonstrates understanding of how loops instantiate multiple objects

Summary

Conditionals (if/else) - Guard clauses, type checking, state validation
Nested Conditions - Grace period + collision check, direction determination
Loops (forEach) - Iterate through game objects and stars
Array Methods - map(), from() for transforming arrays
Complex Logic - Combines loops and conditions for game mechanics