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. |
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.playerHealth and this.enemyHealth store numeric health values.this.gameMode stores a string such as 'singlePlayer' or 'twoPlayer'.this.battleEnded stores a boolean true/false state.this.lasers and this.coins store arrays of active objects.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.
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 - 1 subtracts damage from health.L.x += L.vx and L.y += L.vy move lasers each frame.L.life -= 1 lowers the laser lifetime.this.playerHealth * 100 rewards remaining health in the final score.this.coinCount * this.coinValue adds a coin bonus to the score.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.
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.
&& checks that multiple conditions are true.|| checks if at least one condition is true.! flips a boolean value, such as !this.battleEnded.<=, >=, and === compare values.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.
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.
this.lasers.life value.splice().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.
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.
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 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 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.');
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 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 });
This connects to storing values like player health, score, cooldown timers, coin count, player names, player positions, and game mode.
This connects to how my game uses class properties to store important state inside the level, player, enemy, coins, lasers, and leaderboard systems.
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. |
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.
This connects to logical expressions such as &&, ||, and !,
which are used for attacks, player modes, collision checks, and battle-ending conditions.
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.
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.
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.
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.
This connects directly to the game’s win/loss checks, mode checks, cooldown checks, collision checks, and nested two-player logic.
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.
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.
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.
This connects to arrays like this.lasers, this.coins, this.classes,
and this.leaderboard, which store multiple game objects or records.
This connects to reusable game methods like update(), spawnLaserStraight(),
updateHud(), generateCoins(), saveScore(), and loadLeaderboard().
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.
This connects to the reusable PeppaBattleLevelBase class and the way methods organize repeated
gameplay actions like initializing the level, spawning lasers, updating the HUD, and saving scores.