Group Work On Game Levels — Aryan Malik
A CS111 mini-lesson walkthrough of the three-level slime escape game our group built together. Covers control structures, data types, and operators as they appear in GameLevelCannonball, GameLevelEscaperoom, and GameLevelZonecatch.
THE OBJECTIVE OF THE GAME
This game is really simple and based on objective. Your player is a slime, and the slime is tossed around in the air. Throughout the process, you try to make it fast enough that you don’t melt. You go through three levels:
- A cannonball dodge level
- A maze escape room
- A zone capture challenge
To go past all of them, you reach the sea, so you’re on the surface as fast as possible.
HOW WE USED DEBUGGING
Inspect
The way we use debugging tools in this project is by using the inspect tool in Google Chrome. Using the browser DevTools, we can see the hitboxes for investigation into the code files. We used this to tune collision detection in the cannonball level and to verify that the ZoneCatch overlay canvas was rendering at the correct viewport coordinates.
HTML Component
- File:
_includes/runners/game.html - Reusable component for GameEngine integration
- Automatically creates gameContainer and gameCanvas
- Provides game controls: Start, Pause/Resume, Stop, Reset
- Level selector dropdown for switching between game levels
SCSS Styling
- Main file:
_sass/open-coding/forms/game-runner.scss - Uses runner-base mixin for consistency
- Game output constrained to 400–600 px height for education
- Canvas automatically sized and centered
- Color-coded buttons: Green (Start), Yellow (Pause), Red (Stop)
Game Output Area
The game renders in a constrained canvas for educational purposes:
- Min height: 400 px
- Max height: 600 px
- Canvas max height: 580 px
- Black background with accent-colored border
- Automatically centers the canvas
- Scrollable if content exceeds container
Controls
- ▶ Start: Runs the game code and starts the game engine
- ⏸ Pause / ▶ Resume: Pauses and resumes game execution
- ■ Stop: Stops the game and clears the canvas
- ↻ Reset: Resets code to original and stops the game
- Level Selector: Dropdown to switch between game levels
- 📋 Copy: Copy code to clipboard
- 🗑️ Clear: Clear the editor
Code Structure
Your game code must export two things:
- GameControl: Your GameControl class (usually imported)
- gameLevelClasses: Array of game level classes
import GameControl from '/assets/js/GameEnginev1/essentials/GameControl.js';
import GameLevelCannonball from '/assets/js/GameEnginev1/GameLevelCannonball.js';
import GameLevelEscaperoom from '/assets/js/GameEnginev1/GameLevelEscaperoom.js';
import GameLevelZonecatch from '/assets/js/GameEnginev1/GameLevelZonecatch.js';
export const gameLevelClasses = [GameLevelCannonball, GameLevelEscaperoom, GameLevelZonecatch];
export { GameControl };
CS111 MINI-LESSON: CONTROL STRUCTURES
Mini-lesson purpose: Each section below explains how a CS111 concept appears directly in one of the three game levels.
Iteration — Loops
Iteration means repeating a block of code. The game uses iteration in two main places:
for loop — cannonball lane selection
Inside GameLevelCannonball, three fixed Y-positions are stored in an array and iterated to pick a random lane each shot:
// GameLevelCannonball.js — fireCannonball()
const lanes = [
Math.round(vh * 0.20), // top lane
Math.round(vh * 0.50), // middle lane
Math.round(vh * 0.75) // bottom lane
];
const targetY = lanes[Math.floor(Math.random() * lanes.length)];
Math.floor(Math.random() * lanes.length) produces an index 0, 1, or 2. Using a for-style index into the array is exactly the kind of iteration the rubric calls out: using loops for game-object arrays.
forEach — ZoneCatch sample-point scan
In GameLevelZonecatch, when a round ends, the game samples a cross-shaped set of pixels around the player to decide whether they survived. It iterates a samplePoints array with for...of:
// GameLevelZonecatch.js — _checkSurvivalAtRoundEnd()
const samplePoints = [
[0, 0],
[-10, 0], [10, 0], [0, -10], [0, 10],
[-6, -6], [6, -6], [-6, 6], [6, 6],
];
for (const [dx, dy] of samplePoints) {
const pixel = this.ctx.getImageData(cx + dx, cy + dy, 1, 1).data;
if (pixel[3] < 30) continue; // transparent = outside circles
// ... compare pixel color to safe / danger circle
}
Each iteration reads one pixel under the player’s center. The loop as a whole decides win-or-die.
while-style countdown — ZoneCatch
The countdown before each round uses setTimeout recursion that decrements a counter until it reaches zero, which mirrors a while loop:
// GameLevelZonecatch.js — _runCountdown()
_runCountdown() {
if (this.countdownValue <= 0) {
this.countdownPhase = false;
this._scheduleNextRound(400);
return;
}
this._timer = setTimeout(() => {
this.countdownValue--;
this._runCountdown(); // repeat until countdownValue == 0
}, 1000);
}
Conditionals — if / else
Conditionals control which branch of code runs based on a true/false test.
Cannonball hit-or-dodge:
// GameLevelCannonball.js — _endRound()
if (this.collisionHappened) {
player.x = 100; // reset to start
this.showMessage('💥 HIT! Reset to start.', 'error');
} else {
const advance = player.x >= 700 ? 200 : 300;
player.x += advance;
this.showMessage(`✅ DODGED! +${advance} px!`, 'success');
}
The if/else separates the two game outcomes. This is the core of the level’s feedback loop: one path punishes the player, the other rewards them.
Escaperoom NPC reaction:
// GameLevelEscaperoom.js — npcData1
reaction: function() {
if (this.dialogueSystem) {
this.showReactionDialogue();
} else {
console.log(this.greeting); // fallback for missing dialogue system
}
}
Nested Conditions
Nested conditions are if statements inside other if statements, used when multiple things must all be true.
ZoneCatch survival check — three layers deep:
// GameLevelZonecatch.js — _checkSurvivalAtRoundEnd()
for (const [dx, dy] of samplePoints) {
const pixel = this.ctx.getImageData(cx + dx, cy + dy, 1, 1).data;
// Outer: only consider non-transparent pixels
if (pixel[3] < 30) continue;
hasOpaquePixel = true;
const sd = Math.sqrt(
(pixel[0] - safeRGB.r) ** 2 +
(pixel[1] - safeRGB.g) ** 2 +
(pixel[2] - safeRGB.b) ** 2
);
if (sd < bestSafeDist) bestSafeDist = sd;
// Inner: only compute danger distance if a danger circle exists
if (dangerRGB) {
const dd = Math.sqrt(
(pixel[0] - dangerRGB.r) ** 2 +
(pixel[1] - dangerRGB.g) ** 2 +
(pixel[2] - dangerRGB.b) ** 2
);
if (dd < bestDangerDist) bestDangerDist = dd;
}
}
// Final multi-condition gate
if (!hasOpaquePixel || bestSafeDist > 80 || bestSafeDist >= bestDangerDist) {
this._triggerDeath();
}
The player must satisfy all three conditions simultaneously to survive: there must be a colored pixel under them, it must be close to the safe color, and it must be closer to safe than to danger.
CS111 MINI-LESSON: DATA TYPES
Numbers
Position, velocity, score, and timing are all numbers.
// GameLevelCannonball.js — constructor
this.cannonballX = -300; // start off-screen (negative number)
this.cannonballY = 400;
this.cannonballSpeed = 20; // pixels per frame
this.cannonballSize = 64; // sprite dimensions
// GameLevelZonecatch.js — _spawnCircles()
const baseR = Math.max(50, 95 - this.round * 3); // radius shrinks each round
The cannonball moves because cannonballX -= cannonballSpeed runs every frame inside update().
Strings
Strings hold paths, messages, and color codes.
// GameLevelEscaperoom.js — bgData
src: path + "/images/gamebuilder/bg/Slab.png" // path concatenation
// GameLevelCannonball.js — showMessage()
this.showMessage('💥 HIT! Reset to start.', 'error');
this.showMessage(`✅ DODGED! +${advance} px!`, 'success'); // template literal
// GameLevelZonecatch.js — colorPairs
['#e63946', '#457b9d'], // hex color strings used to fill circles
Template literals (the backtick strings) let the game build messages that include live variable values — the dodge distance changes based on how far the player has traveled.
Booleans
Boolean flags control the game’s state machine.
// GameLevelCannonball.js — constructor
this.roundRunning = false; // is a round currently active?
this.dodgeWindowOpen = false; // can the cannonball register a hit?
this.collisionHappened = false; // did the player get hit this round?
// GameLevelZonecatch.js — _reset()
this.roundActive = false;
this.breakActive = false;
this.gameOver = false;
this.won = false;
this.introPhase = true;
Every major game-state transition is a boolean flip. For example, when a round ends:
this.roundActive = false;
this.breakActive = true;
Arrays
Arrays hold collections of game objects and data.
// GameLevelZonecatch.js — colorPairs: array of color-pair arrays
this.colorPairs = [
['#e63946', '#457b9d'],
['#f4a261', '#2a9d8f'],
['#e9c46a', '#6a0572'],
['#ff006e', '#38b000'],
['#fb5607', '#3a86ff'],
['#ffbe0b', '#8338ec'],
];
// circles array holds the two active zone circles each round
this.circles = [
{ x: x0, y: y0, r: baseR, color: shuffled[0], safe: safeIdx === 0 },
{ x: x1, y: y1, r: baseR, color: shuffled[1], safe: safeIdx === 1 },
];
// this.classes is the array the engine reads to build the level
this.classes = [
{ class: GameEnvBackground, data: bgData },
{ class: Player, data: playerData },
{ class: Barrier, data: barrier1 },
// ... more barriers
];
Objects (JSON)
Configuration objects describe every game entity’s properties.
// GameLevelEscaperoom.js — playerData config object
const playerData = {
id: 'playerData',
src: path + "/images/gamebuilder/sprites/slime.png",
SCALE_FACTOR: 15,
STEP_FACTOR: 1000,
ANIMATION_RATE: 50,
INIT_POSITION: { x: 60, y: 247 }, // nested object
pixels: { height: 225, width: 225 },
orientation: { rows: 4, columns: 4 },
hitbox: { widthPercentage: 0, heightPercentage: 0 },
keypress: { up: 87, left: 65, down: 83, right: 68 }
};
Changing INIT_POSITION.x moves the player’s spawn. Changing STEP_FACTOR makes movement faster. The entire level is data-driven through these objects.
CS111 MINI-LESSON: OPERATORS
Mathematical Operators
Physics and position use +, -, *, /, and Math functions.
// GameLevelCannonball.js — update() — moves the cannonball left every frame
this.cannonballX -= this.cannonballSpeed;
// AABB collision — checks rectangle overlap
const pb = {
x: px + sx,
y: py + sy,
x2: px + pw - sx,
y2: py + ph - sy
};
return !(cb.x2 < pb.x || cb.x > pb.x2 || cb.y2 < pb.y || cb.y > pb.y2);
// GameLevelZonecatch.js — Euclidean distance between two points
_dist(ax, ay, bx, by) {
return Math.sqrt((ax - bx) ** 2 + (ay - by) ** 2);
}
// Circle radius shrinks each round using subtraction and Math.max
const baseR = Math.max(50, 95 - this.round * 3);
String Operations
Path concatenation and template literals build file paths and UI messages.
// Path concatenation — string + string
src: this.path + "/images/gamebuilder/sprites/Cannonball.png"
// Template literal — embeds a variable directly into a string
this.showMessage(`✅ DODGED! +${advance} px!`, 'success');
// ZoneCatch — color hex string used as Canvas fill
ctx.fillStyle = safe.color + 'cc'; // append alpha to hex color string
Boolean Expressions (&&, ||, !)
&& (AND) — all must be true:
// GameLevelZonecatch.js — player must be near the gate AND gate must be visible
if (this.roundActive && this.gateVisible && this.gateCircle) {
const p = this._getPlayerCenter();
if (p && this._dist(p.x, p.y, this.gateCircle.x, this.gateCircle.y)
< this.gateCircle.r * 1.5) {
this._triggerWin();
}
}
|| (OR) — any one is enough:
// ZoneCatch death condition — any one of three problems kills the player
if (!hasOpaquePixel || bestSafeDist > 80 || bestSafeDist >= bestDangerDist) {
this._triggerDeath();
}
! (NOT) — flips a boolean:
// GameLevelZonecatch.js — spawn circles, one safe and one not safe
{ x: x0, y: y0, r: baseR, color: shuffled[0], safe: safeIdx === 0 },
{ x: x1, y: y1, r: baseR, color: shuffled[1], safe: safeIdx === 1 },
// The danger circle is found by negating .safe
const danger = this.circles.find(c => !c.safe);
Level Breakdown
Level 1 — GameLevelCannonball (Dodge Challenge)
Challenge: Use W and S to move up and down. A cannonball fires from the right — dodge it to advance 300 px toward the gate. Get hit and you reset to the start. Reach the gate and press E, then ESC to advance.
📄 View Level 1 Source Code
Key CS111 concepts shown:
- Iteration: Lane array looped to pick a random cannonball path
- Conditionals: Hit vs dodge branches reward/punish the player
- Numbers:
cannonballX -= cannonballSpeedmoves the ball 20 px per frame - Booleans:
dodgeWindowOpen,collisionHappened,roundRunningtrack round state - Mathematical operators: AABB rectangle overlap uses
<,>, subtraction, and||
Level 2 — GameLevelEscaperoom (Maze)
Challenge
Key CS111 concepts shown:
- Objects (JSON): Every barrier is a config object with
x,y,width,height, andhitbox - Arrays:
this.classesholds 20 entries — 1 background, 1 player, 1 NPC, 17 barriers - Strings: Sprite
srcpaths built withpath + "/images/..."concatenation - Numbers: Barrier dimensions (e.g.
width: 1134,height: 50) set collision boundaries - Conditionals: NPC
reactionchecksif (this.dialogueSystem)before showing dialogue
Level 3 — GameLevelZonecatch (Zone Capture)
Challenge: Two colored zones appear on screen. The banner at the top tells you which color is SAFE. Move into the safe zone before the timer bar empties. Stand in the wrong zone or outside both — eliminated. From Round 6 a golden GATE appears: reach it and press E to escape early and win!