โ† Home    Full Blog

Control Structures

Iteration ยท Conditionals ยท Nested Conditions

๐Ÿ“‹ Table of Contents

  1. Iteration
  2. Conditionals
  3. Nested Conditions

๐Ÿ” Iteration

Loops are used everywhere in the game โ€” to move lasers, check collisions, and iterate over game object arrays. The game uses for, forEach, and while-style animation loops.

for loop โ€” iterate backwards through laser array

// Iterate backwards through the lasers array โ€” safe to splice while looping
for (let i = this.lasers.length - 1; i >= 0; i--) {
  const L = this.lasers[i];

  L.x    += L.vx;        // move laser each frame
  L.y    += L.vy;
  L.life -= 1;           // count down lifespan

  if (L.life <= 0) {
    this.lasers.splice(i, 1);   // remove expired laser
    continue;
  }
  // ... collision checks below ...
}
This for loop moves every laser every frame. Iterating backwards is essential: when splice(i, 1) removes a laser, everything above that index shifts down by one. A forward loop would skip the element that just moved into index i. Backwards iteration means only already-visited indices shift, so nothing is missed.

forEach โ€” iterating over game object collections

// forEach iterates all active lasers to draw them on the canvas
this.lasers.forEach(L => {
  const ctx = this.gameEnv?.ctx;
  if (!ctx) return;
  ctx.fillStyle = L.isPlayerLaser ? '#58a6ff' : '#ff7b72';  // blue=player, red=enemy
  ctx.beginPath();
  ctx.arc(L.x, L.y, 5, 0, Math.PI * 2);
  ctx.fill();
});

// forEach also used in leaderboard rendering
scores.forEach(({ player, score }, index) => {
  console.log(`#${index + 1} ${player}: ${score}`);
});
forEach is used when you need to process every element and don't need to break early or splice. For rendering, every laser needs to be drawn without any removal logic โ€” forEach is the clean choice. For the for loop used during collision/removal, forEach can't be used because you need the index i to call splice.

Code Runner โ€” Iteration Practice

Practice iterating backwards through an array and safely removing elements โ€” the same pattern used for lasers every frame.

Lines: 1Chars: 0
Output
Click โ–ถ to run ...

while-style animation loop โ€” game engine frame loop

// The game engine drives all updates in a requestAnimationFrame loop
// equivalent to: while (game is running) { update all objects; draw frame; }
function gameLoop() {
  if (!this.running) return;   // while condition check
  this.update();                  // update all game objects
  requestAnimationFrame(this.gameLoop.bind(this));
}

// Inside PeppaBattleLevelBase.update() โ€” called 60x/sec by the loop
update() {
  if (this.battleEnded) return;
  this.updateLasers();    // runs the for loop above
  this.drawLasers();      // runs the forEach above
  this.checkBossDefeat();
}
The game engine uses requestAnimationFrame which calls the update function 60 times per second โ€” structurally identical to a while(running) loop. Every laser, every boss movement, every collision check happens inside this loop. Without iteration, nothing in the game would move.

๐Ÿ”€ Conditionals

Conditionals control every state transition in the game โ€” when the battle ends, whether the player wins or loses, and which level loads next. They use if/else chains and guard clauses.

if/else โ€” state transitions in the game loop

update() {
  // Conditional 1: guard clause โ€” stop everything if battle is over
  if (this.battleEnded) return;

  // Conditional 2: check win condition
  if (boss.isDefeated && !this.battleEnded) {
    this.battleEnded = true;
    const isFinalLevel = ctrl.currentLevelIndex === ctrl.levelClasses?.length - 1;
    if (isFinalLevel) {
      this.showWinScreen();
    } else {
      this.messageTimeout = setTimeout(() => {
        if (ctrl?.currentLevel) ctrl.currentLevel.continue = false;
      }, 1100);
    }
    return;
  }

  // Conditional 3: lose condition
  if (this.playerHealth <= 0 && !this.battleEnded) {
    this.battleEnded = true;
    this.showLoseScreenAndRestart();
  }
}
The three conditionals are the entire win/lose logic. The guard clause at the top stops the game loop once the battle ends. The if/else in the win path decides whether to show the final win screen or advance to the next level.

Try It โ€” Conditionals

Run this to see the win/lose logic. Try changing playerHealth to 0 to trigger the lose path instead.

Lines: 1Characters: 0
Output
Click "Run" in code control panel to see output ...

๐Ÿ”€ Nested Conditions

Nested conditions implement complex game logic where multiple state checks must pass simultaneously before an action fires โ€” like power-up activation, attack cooldowns, or multi-stage collision detection.

Nested conditions โ€” attack cooldown with compound checks

// Outer condition: player pressed SPACE this frame
if (this.attackRequested) {
  this.attackRequested = false;

  // Inner condition: cooldown elapsed AND boss is still alive
  if (now - this.lastAttackAt >= this.attackCooldownMs && !boss.isDefeated) {
    this.lastAttackAt = now;
    this.spawnLaserStraight(px, py, player.direction, true);
  }
}
These two layers of conditions prevent infinite laser spam. The outer check confirms the player pressed SPACE this frame. The inner check uses && to require both: enough time since the last shot AND the boss isn't already dead. If either inner condition fails, no laser fires and lastAttackAt doesn't reset โ€” the cooldown keeps ticking. This nested structure is what gives combat its rhythm.

Multi-level conditions โ€” power-up + collision + direction

// AABB collision โ€” 4 conditions must all be true simultaneously
if (hitLeft         <  boss.position.x + boss.width  &&
    hitLeft + hitW   >  boss.position.x               &&
    hitTop           <  boss.position.y + boss.height  &&
    hitTop  + hitH   >  boss.position.y) {

  // Nested: only player lasers damage boss, only enemy lasers damage player
  if (L.isPlayerLaser) {
    boss.takeDamage(1);

    // Nested deeper: only flash if boss is still vulnerable after the hit
    if (!boss.isDefeated) {
      this.flashDamageIndicator();
    }
  } else {
    this.damagePlayer(1);
  }

  this.lasers.splice(i, 1);
}
This is the deepest nesting in the game โ€” three levels of conditions. The outer AABB check confirms the rectangles overlap. The middle check reads isPlayerLaser to decide who takes damage. The innermost check avoids flashing a damage indicator on an already-defeated boss. Each level of nesting is necessary โ€” collapsing them into one flat if-chain would require many more conditions and lose the logical structure.

Code Runner โ€” Nested Conditions Practice

Simulate the attack cooldown logic with nested if/else conditions โ€” the same pattern that gates laser firing in the game.

Lines: 1Chars: 0
Output
Click โ–ถ to run ...

๐Ÿ“š Related Homework: Nested Conditionals

Nested Conditionals Homework โ€” Demonstrates complex decision-making logic using nested if-else statements, directly applicable to boss behavior and win/lose conditions.

๐Ÿ“š Related Homework: Iterations

Iterations Homework โ€” Shows proficiency in loops and iteration, essential for game loops, collision detection, and processing arrays of game objects.