🔀

Operators & Control Structures

This page explains how operators, data, loops, conditionals, and nested logic control my Peppa Pig game. Each section connects a programming concept to a real gameplay feature.

Learning Objective Project Evidence Required Assessment Method
Data and State Section 1 explains variables, strings, numbers, booleans, arrays, and objects used to track gameplay. Review stored values and game-state organization.
Mathematical Operators Section 2 explains operators that calculate damage, movement, distance, score, velocity, and laser life. Code review of formulas.
Boolean Expressions Section 3 explains boolean expressions for attacks, game mode, death states, and boss defeat. Review &&, ||, !, and comparisons.
Iteration Section 4 explains loops that update lasers, coins, objects, and game logic. Code review of loop-based systems.
Conditionals Section 5 explains if/else logic for win/loss, player mode, cooldowns, and input handling. Review conditionals and nested logic.

Specific Operators & Control Structures Requirements


1. Data and State

Requirement: Use variables, strings, numbers, booleans, arrays, and objects

Data and state are the stored values that let the game remember what is happening. My game uses variables to track health, score, cooldowns, lasers, coins, game mode, battle status, player names, and object data. These values change during gameplay, which is why they are important game state.

this.gameMode = 'twoPlayer';       // String
this.playerHealth = 4;             // Number
this.battleEnded = false;          // Boolean
this.lasers = [];                  // Array

this.lasers.push({
    x: fromX,
    y: fromY,
    vx: vx,
    vy: vy,
    source: 'player1',
    life: 60
});

This proves the data and state requirement because the game uses different data types to store and update gameplay information every frame.

2. Mathematical Operators

Requirement: Calculate damage, movement, score, velocity, and laser life

Mathematical operators are used when the game changes numbers. My project uses subtraction for health and laser life, addition assignment for movement, multiplication for score, division for player center position, and comparison operators for cooldowns.

this.playerHealth = Math.max(0, this.playerHealth - 1);

L.x += L.vx;
L.y += L.vy;
L.life -= 1;

const score =
    (this.playerHealth * 100) +
    (this.coinCount * this.coinValue);

This proves the mathematical operators requirement because the game uses operators to calculate health, laser movement, projectile lifetime, and the final score.

3. Boolean Expressions

Requirement: Use comparisons, compound checks, and logical operators

Boolean expressions are conditions that evaluate to true or false. My game uses boolean expressions to decide whether objects exist, whether the battle is over, whether a boss is defeated, whether a player is dead, and whether the correct game mode is active.

const player1Dead = this.playerHealth <= 0;
const player2Dead = player2 && this.player2Health <= 0;

if ((player1Dead || player2Dead) && !this.battleEnded) {
    this.battleEnded = true;
}

if (boss && !boss.isDefeated) {
    this.updateHud('Boss is still active.');
}

This proves the boolean expressions requirement because the game uses logical operators and comparisons to control attacks, death checks, boss defeat, and battle-ending behavior.

4. Iteration

Requirement: Loop through lasers, coins, objects, and update logic

Iteration means repeating code, usually with a loop. My game uses iteration to update every active laser, check collections like coins, and process lists of game objects. This is important because there can be many lasers or coins on screen at the same time.

for (let i = this.lasers.length - 1; i >= 0; i--) {
    const L = this.lasers[i];

    L.x += L.vx;
    L.y += L.vy;
    L.life -= 1;

    if (L.life <= 0) {
        this.lasers.splice(i, 1);
        continue;
    }
}

This proves the iteration requirement because the game repeatedly updates every laser in the array instead of writing separate movement code for each laser.

5. Conditionals

Requirement: Use if / else logic for modes, cooldowns, input, wins, and losses

Conditionals decide which branch of code should run. My game uses conditionals for single-player versus two-player mode, attack cooldowns, menu controls, win/loss messages, collision checks, and preventing errors when game objects are not loaded yet.

if (this.gameMode === 'twoPlayer') {
    if (!player || !player2) return;
} else {
    if (!player || !boss) return;
}
handleKeyDown(event) {
    if (event.key === 'ArrowUp') {
        this.selectedOption = (this.selectedOption - 1 + this.options.length) % this.options.length;
        this.renderOptions();
    } else if (event.key === 'ArrowDown') {
        this.selectedOption = (this.selectedOption + 1) % this.options.length;
        this.renderOptions();
    } else if (event.key === 'Enter') {
        this.selectPower();
    } else if (event.key === 'Escape') {
        event.preventDefault();
    }

    event.preventDefault();
}

This proves the conditionals requirement because the game uses if, else if, and nested logic to decide how the game should respond to different situations.

🧩

Game Data and State


My game depends on organized state. Instead of every feature being random code, important values are stored in variables, arrays, and objects so the game can remember what is happening every frame. Health, score, cooldowns, coins, player names, and level settings all work together to control the battle.

Numbers

Health, Score, Speed, and Timers

Numbers control the actual gameplay. Health goes down after damage, laser position changes every frame, cooldown timers decide when attacks can happen again, and the final score is calculated from health and coins.

this.playerHealth = 4;
this.coinValue = 25;
this.attackCooldownMs = 450;

const score = (this.playerHealth * 100) + (this.coinCount * this.coinValue);
Strings

Names, Modes, and Messages

Strings make the game easier to customize. They store the player name, game mode, level ID, enemy name, and HUD messages that appear while playing.

this.gameMode = 'twoPlayer';
this.playerName = 'Player';
this.updateHud('Laser hit! Enemy took damage.');
Booleans

Game Flow Flags

Boolean values decide whether the game should keep running, whether a laser belongs to the player, whether the battle has ended, and whether a player is allowed to attack.

this.battleEnded = false;
this.attackRequested = true;

if (!this.battleEnded) {
    this.updateHud('Battle is still active.');
}
Arrays and Objects

Collections of Game Objects

Arrays let the game manage multiple lasers, coins, classes, and leaderboard entries. Objects store grouped information like position, velocity, level settings, and score data.

this.lasers = [];
this.coins = [];

this.lasers.push({
    x: fromX,
    y: fromY,
    vx: vx,
    vy: vy,
    source: 'player1',
    life: 60
});

Operators


Operators are what turn stored data into movement, damage, scoring, and collision checks. In my game, operators are not only used for simple math. They decide how far lasers move, when cooldowns finish, how health changes, and how the final score is created.

Operator Concept Where It Appears in My Game Why It Matters
Subtraction this.playerHealth - 1 Reduces health after a laser or enemy hit.
Addition Assignment L.x += L.vx and L.y += L.vy Moves lasers smoothly across the screen every frame.
Multiplication this.playerHealth * 100 Rewards the player for surviving with more health.
Comparison now - this.lastAttackAt >= this.attackCooldownMs Checks whether enough time has passed before another attack is allowed.
Logical Operators boss && !boss.isDefeated Makes sure actions only happen when the correct objects exist and the game state allows it.

Score and Movement Example

this.playerHealth = Math.max(0, this.playerHealth - 1);
L.x += L.vx;
L.y += L.vy;
L.life -= 1;

const score = (this.playerHealth * 100) + (this.coinCount * this.coinValue);

This code shows multiple operator types working together. Health is lowered, laser position is updated, laser lifetime counts down, and the final score combines survival with coin collection. The use of Math.max() also prevents health from becoming negative, which keeps the game state cleaner.


🔀

Control Structures


Control structures decide what the game should do next. My project uses conditionals to switch between single-player and two-player logic, check win and loss states, stop actions after the battle ends, and make sure attacks follow cooldown rules.

Mode-Based Game Logic

if (this.gameMode === 'twoPlayer') {
    if (!player || !player2) return;
} else {
    if (!player || !boss) return;
}

This condition makes the update loop smarter. In two-player mode, the game waits for both players. In single-player mode, it waits for the player and boss. This prevents errors because the game does not try to update objects that have not loaded yet.

Different Endings Based on Player State

const player1Dead = this.playerHealth <= 0;
const player2Dead = player2 && this.player2Health <= 0;

if ((player1Dead || player2Dead) && !this.battleEnded) {
    this.battleEnded = true;

    if (this.gameMode === 'twoPlayer') {
        if (player1Dead && player2Dead) {
            lossMessage = 'Draw! Both players defeated! Restarting...';
        } else if (player1Dead) {
            lossMessage = 'Player 2 Wins! Player 1 defeated. Restarting...';
        } else {
            lossMessage = 'Player 1 Wins! Player 2 defeated. Restarting...';
        }
    }
}

This is stronger than a simple win-or-lose check because the message depends on the exact situation. The game can detect a draw, a Player 1 win, or a Player 2 win. That makes the two-player mode feel more complete and intentional.

Menu Control with Arrow Keys and Enter

handleKeyDown(event) {
    if (event.key === 'ArrowUp') {
        this.selectedOption = (this.selectedOption - 1 + this.options.length) % this.options.length;
        this.renderOptions();
    } else if (event.key === 'ArrowDown') {
        this.selectedOption = (this.selectedOption + 1) % this.options.length;
        this.renderOptions();
    } else if (event.key === 'Enter') {
        this.selectPower();
    } else if (event.key === 'Escape') {
        event.preventDefault();
    }

    event.preventDefault();
}

This method handles several keyboard choices in one place. The modulo operator keeps the menu selection inside the option list, so the player can cycle through choices without breaking the menu.


🎮

Game Loop and Iteration


The update() method is the part of the game that runs repeatedly. It acts like the engine of the battle because it checks inputs, moves lasers, updates cooldowns, checks collisions, and refreshes the HUD. Without the game loop, the project would only show still objects instead of an active battle.

Attack Cooldown Logic

if (this.attackRequested) {
    this.attackRequested = false;

    if (now - this.lastAttackAt >= this.attackCooldownMs) {
        this.lastAttackAt = now;

        const px = player.position.x + player.width / 2;
        const py = player.position.y + player.height / 2;
        const dir = player.direction || 'right';

        this.spawnLaserStraight(px, py, dir, true, 'player1');
    }
}

This code prevents attack spam. The first condition checks whether the player requested an attack. The second condition checks whether enough time has passed since the last attack. This creates a more balanced game because the player has to time shots instead of holding one key forever.

Laser List Updates

for (let i = this.lasers.length - 1; i >= 0; i--) {
    const L = this.lasers[i];

    L.x += L.vx;
    L.y += L.vy;
    L.life -= 1;

    if (L.life <= 0) {
        this.lasers.splice(i, 1);
        continue;
    }
}

This loop updates every laser in the array. Each laser moves, loses lifetime, and gets removed when it expires. Looping backward is useful because removing an item with splice() will not accidentally skip the next laser in the list.


🏗️

Object-Oriented Structure


My project also connects to object-oriented programming because the battle logic is organized into reusable classes and methods. Instead of rewriting the same gameplay code for every level, shared logic can stay in a base class while each level changes its own settings, enemy health, speed, images, and title.

class PeppaBattleLevelBase {
    constructor(config) {
        this.config = config;
        this.playerHealth = config.playerHealth ?? 4;
        this.enemyHealth = config.enemyHealth ?? 5;
        this.coinValue = config.coinValue ?? 25;
    }
}

This design makes the game easier to expand. A new level can reuse the same battle system while changing only the configuration values. That is cleaner than duplicating all the movement, collision, coin, and scoring code.

← Back to College Ready Blog