Checklist
Writing Classes
- Create minimum 2 custom character classes extending base classes
- Code review: Player.js, NPC.js, Enemy.js,
- Found in any level
Level 1: Character Classes
Challenge
2 Classes Demo
Lines: 1
Characters: 0
Game Status:
Not Started
Methods & Parameters
- Implement methods with parameters (e.g., collisionHandler(other, direction))
- Code review: Method signatures with 2+ parameters
Possibly battle bus in level 3??
Instantiation & Objects
- Instantiate game objects in GameLevel configuration
- Code review: GameLevel setup objects
Level 1: The constructer creates data objects
Inheritance (Basic)
- Create class hierarchy with 2+ levels (e.g., GameObject → Character → Player)
- Code review: extends keyword, inheritance chain
Challenge
Wall repetition, rather than individually placing them
Lines: 1
Characters: 0
Game Status:
Not Started
Challenge
Step Counter Demo
Lines: 1
Characters: 0
Game Status:
Not Started
Method Overriding
- Override parent methods (update(), draw(), handleCollision())
- Code review: Polymorphic implementations
Challenge
Step Counter Demo
Lines: 1
Characters: 0
Game Status:
Not Started
- Or additionally, we use gravity.
update() { super.update(); if(!this.moved){ if (this.gravity) { this.time += 1; this.velocity.y += 0.5 + this.acceleration * this.time; } } else{ this.time = 0; } }
Constructor Chaining
- Use super() to chain constructors
- Code review: super(data, gameEnv) calls
constructor(data = null, gameEnv = null) { super(data, gameEnv); this.interact = data?.interact; // Interact function this.currentQuestionIndex = 0; this.alertTimeout = null; this.isInteracting = false; // Flag to track if currently interacting this.handleKeyDownBound = this.handleKeyDown.bind(this); this.handleKeyUpBound = this.handleKeyUp.bind(this); this.bindInteractKeyListeners();
CONTROL STRUCTURES
Iteration
- Use loops for game object arrays, animation frames
- Code review: for, forEach, while loops
Level 1: Wall Classes
- repetition of walls
const wallClasses = mazeWalls.map(wall => ({ ... }));
%%js
// GAME_RUNNER: Wall repetition, rather than individually placing them
// Import for GameRunner
import GameControl from '/assets/js/GameEnginev1/essentials/GameControl.js';
// Level Code
import GameEnvBackground from '/assets/js/GameEnginev1/essentials/GameEnvBackground.js';
import Player from '/assets/js/GameEnginev1/essentials/Player.js';
import Barrier from '/assets/js/GameEnginev1.1/essentials/Barrier.js'
class CustomLevel {
constructor(gameEnv) {
const path = gameEnv.path;
const width = gameEnv.innerWidth;
const height = gameEnv.innerHeight;
const bgData = {
name: 'custom_bg',
src: path + "/images/gamebuilder/bg/clouds.jpg",
pixels: { height: 720, width: 1280 }
};
const playerData = {
id: 'playerData',
src: path + "/images/gamebuilder/sprites/kirby.png",
SCALE_FACTOR: 5,
STEP_FACTOR: 1000,
ANIMATION_RATE: 50,
INIT_POSITION: { x: 100, y: 300 },
pixels: { height: 36, width: 569 },
orientation: { rows: 1, columns: 13 },
down: { row: 0, start: 0, columns: 3 },
downRight: { row: 0, start: 0, columns: 3, rotate: Math.PI/16 },
downLeft: { row: 0, start: 0, columns: 3, rotate: -Math.PI/16 },
left: { row: 0, start: 0, columns: 3 },
right: { row: 0, start: 0, columns: 3 },
up: { row: 0, start: 0, columns: 3 },
upLeft: { row: 0, start: 0, columns: 3, rotate: Math.PI/16 },
upRight: { row: 0, start: 0, columns: 3, rotate: -Math.PI/16 },
hitbox: { widthPercentage: 0, heightPercentage: 0 },
keypress: { up: 87, left: 65, down: 83, right: 68 }
};
const mazeWalls = [
{ x: 0, y: 0, width: width, height: 20 },
{ x: 0, y: height - 20, width: width, height: 20 },
{ x: width * 0.2, y: 0, width: 20, height: height * 0.6 },
{ x: width * 0.4, y: height * 0.4, width: 20, height: height * 0.6 },
{ x: width * 0.6, y: 0, width: 20, height: height * 0.6 },
{ x: width * 0.8, y: height * 0.4, width: 20, height: height * 0.6 }
];
const wallClasses = mazeWalls.map(wall => ({
class: Barrier,
data: { id: "wall_" + Math.random(), x: wall.x, y: wall.y, width: wall.width, height: wall.height, visible: false }
}));
this.classes = [
{ class: GameEnvBackground, data: bgData },
{ class: Player, data: playerData },
...wallClasses
];
}
}
export const gameLevelClasses = [CustomLevel];
export { GameControl };
Conditionals
- Implement collision detection, state transitions
- Code review: if/else, nested conditions
Level 2: Message
if (steps > STEP_GOAL) { message.textContent = "You didn't make it..."; gameOver = true; }
Nested Conditions
- Complex game logic (e.g., power-up + collision + direction)
- Code review: Multi-level conditionals
Level 2: Teleporting Garrett
reaction: function() { if (!this.teleported) return; // Level 1 if (window.currentSteps <= window.stepGoal) { // Level 2 alert("Win"); } else { alert("Loss"); } }
DATA TYPES
Numbers
- Position, velocity, score tracking
- Code review: Numeric properties
Level 2: Step Counter
const STEP_GOAL = 200; steps++;
Strings
- Character names, sprite paths, game states
- Code review: String manipulation
Level 2: Message
message.textContent = "You didn't make it to Garrett in time!"; window.location.href = "timmycounter.html";Booleans
- Flags (isJumping, isPaused, isVulnerable)
- Code review: Boolean logic
Level 2: Music!
let musicStarted = false; let gameOver = false; this.teleported = true;
Arrays
- Game object collections, level data
- Code review: Array operations
Level 1: Player Data
this.classes = [ { class: GameEnvBackground, data: bgData },
{ class: Player, data: playerData },
{ class: Npc, data: npcData1 },
{ class: Npc, data: npcData2 },
{ class: Npc, data: npcData3 },
{ class: Barrier, data: dbarrier_1 }
];
];
Objects (JSON)
- Configuration objects, sprite data
- Code review: Object literals
Level 1: Any NPC or Player data, EX is from Garrett’s data
const npcData1 = { id: 'Garrett The Popcorn Man', greeting: 'Hi! I\'m Garrett!', src: path + "/images/gamebuilder/sprites/GarettThePopcornMan.png", SCALE_FACTOR: 1, ANITION_RATE: 50, INIT_POSITION: { x: 650, y: 540 }, pixels: { height: 523, width: 477 }, orientation: { rows: 1, columns: 1 }, down: { row: 0, start: 0, columns: 1 }, hitbox: { widthPercentage: 0.1, heightPercentage: 0.2 }, dialogues: [ "Welcome to Timmy's Fun World! I'm Garrett! Oh, and by the way, be wary of that circus tent, the Invisible Maze lies within... Want some popcorn?", ], reaction: function() { if (this.dialogueSystem) { this.showReactionDialogue(); } else { console.log(this.greeting); } }, interact: function() { if (this.dialogueSystem) { this.showRandomDialogue(); } } };
OPERATORS
Mathematical
- Physics calculations (gravity, velocity, collision)
- Code review: +, -, *, / in physics
Level 2: Step Counter
%%js
// GAME_RUNNER: Step Counter Demo
// Import for GameRunner
import GameControl from '/assets/js/GameEnginev1/essentials/GameControl.js';
// Level Code
import GameEnvBackground from '/assets/js/GameEnginev1/essentials/GameEnvBackground.js';
import Player from '/assets/js/GameEnginev1/essentials/Player.js';
import Barrier from '/assets/js/GameEnginev1.1/essentials/Barrier.js'
class CustomLevel {
constructor(gameEnv) {
const path = gameEnv.path;
const width = gameEnv.innerWidth;
const height = gameEnv.innerHeight;
const bgData = {
name: 'custom_bg',
src: path + "/images/gamebuilder/bg/clouds.jpg",
pixels: { height: 720, width: 1280 }
};
const playerData = {
id: 'playerData',
src: path + "/images/gamebuilder/sprites/kirby.png",
SCALE_FACTOR: 5,
STEP_FACTOR: 1000,
ANIMATION_RATE: 50,
INIT_POSITION: { x: 100, y: 300 },
pixels: { height: 36, width: 569 },
orientation: { rows: 1, columns: 13 },
down: { row: 0, start: 0, columns: 3 },
downRight: { row: 0, start: 0, columns: 3, rotate: Math.PI/16 },
downLeft: { row: 0, start: 0, columns: 3, rotate: -Math.PI/16 },
left: { row: 0, start: 0, columns: 3 },
right: { row: 0, start: 0, columns: 3 },
up: { row: 0, start: 0, columns: 3 },
upLeft: { row: 0, start: 0, columns: 3, rotate: Math.PI/16 },
upRight: { row: 0, start: 0, columns: 3, rotate: -Math.PI/16 },
hitbox: { widthPercentage: 0, heightPercentage: 0 },
keypress: { up: 87, left: 65, down: 83, right: 68 }
};
window.addEventListener("load", () => {
// 1. Browser Alert - This acts as the "user interaction" needed to play audio
alert("Catch me if you can! -Garrett");
// 2. Play music immediately after clicking 'OK'
music.play().catch(err => console.log("Audio waiting for interaction:", err));
const STEP_GOAL = 300;
window.currentSteps = 0;
window.stepGoal = STEP_GOAL;
this.createLeaderboardUI();
const hud = document.createElement("div");
hud.style.cssText = "position:fixed; bottom:20px; left:50%; transform:translateX(-50%); z-index:10000;";
document.body.appendChild(hud);
const stepCounterEl = document.createElement("div");
stepCounterEl.style.cssText = "color:white; font-size:26px; font-family:Arial; background:rgba(0,0,0,0.85); padding:10px 18px; border-radius:10px; border: 2px solid #ffd700;";
stepCounterEl.textContent = "Steps: 0 / " + STEP_GOAL;
hud.appendChild(stepCounterEl);
document.addEventListener("keydown", (e) => {
const movementKeys = [87, 65, 83, 68];
if (movementKeys.includes(e.keyCode)) {
window.currentSteps++;
stepCounterEl.textContent = `Steps: ${window.currentSteps} / ${STEP_GOAL}`;
if (window.currentSteps > STEP_GOAL * 0.8) {
stepCounterEl.style.color = "#ff4d4d";
}
}
});
});
this.classes = [
{ class: GameEnvBackground, data: bgData },
{ class: Player, data: playerData },
];
}
}
export const gameLevelClasses = [CustomLevel];
export { GameControl };
%%js
// GAME_RUNNER: Step Counter Demo
// Import for GameRunner
import GameControl from '/assets/js/GameEnginev1/essentials/GameControl.js';
// Level Code
import GameEnvBackground from '/assets/js/GameEnginev1/essentials/GameEnvBackground.js';
import Player from '/assets/js/GameEnginev1/essentials/Player.js';
import Barrier from '/assets/js/GameEnginev1.1/essentials/Barrier.js'
class CustomLevel {
constructor(gameEnv) {
const path = gameEnv.path;
const width = gameEnv.innerWidth;
const height = gameEnv.innerHeight;
const bgData = {
name: 'custom_bg',
src: path + "/images/gamebuilder/bg/clouds.jpg",
pixels: { height: 720, width: 1280 }
};
const playerData = {
id: 'playerData',
src: path + "/images/gamebuilder/sprites/kirby.png",
SCALE_FACTOR: 5,
STEP_FACTOR: 1000,
ANIMATION_RATE: 50,
INIT_POSITION: { x: 100, y: 300 },
pixels: { height: 36, width: 569 },
orientation: { rows: 1, columns: 13 },
down: { row: 0, start: 0, columns: 3 },
downRight: { row: 0, start: 0, columns: 3, rotate: Math.PI/16 },
downLeft: { row: 0, start: 0, columns: 3, rotate: -Math.PI/16 },
left: { row: 0, start: 0, columns: 3 },
right: { row: 0, start: 0, columns: 3 },
up: { row: 0, start: 0, columns: 3 },
upLeft: { row: 0, start: 0, columns: 3, rotate: Math.PI/16 },
upRight: { row: 0, start: 0, columns: 3, rotate: -Math.PI/16 },
hitbox: { widthPercentage: 0, heightPercentage: 0 },
keypress: { up: 87, left: 65, down: 83, right: 68 }
};
window.addEventListener("load", () => {
// 1. Browser Alert - This acts as the "user interaction" needed to play audio
alert("Catch me if you can! -Garrett");
// 2. Play music immediately after clicking 'OK'
music.play().catch(err => console.log("Audio waiting for interaction:", err));
const STEP_GOAL = 300;
window.currentSteps = 0;
window.stepGoal = STEP_GOAL;
this.createLeaderboardUI();
const hud = document.createElement("div");
hud.style.cssText = "position:fixed; bottom:20px; left:50%; transform:translateX(-50%); z-index:10000;";
document.body.appendChild(hud);
const stepCounterEl = document.createElement("div");
stepCounterEl.style.cssText = "color:white; font-size:26px; font-family:Arial; background:rgba(0,0,0,0.85); padding:10px 18px; border-radius:10px; border: 2px solid #ffd700;";
stepCounterEl.textContent = "Steps: 0 / " + STEP_GOAL;
hud.appendChild(stepCounterEl);
document.addEventListener("keydown", (e) => {
const movementKeys = [87, 65, 83, 68];
if (movementKeys.includes(e.keyCode)) {
window.currentSteps++;
stepCounterEl.textContent = `Steps: ${window.currentSteps} / ${STEP_GOAL}`;
if (window.currentSteps > STEP_GOAL * 0.8) {
stepCounterEl.style.color = "#ff4d4d";
}
}
});
});
this.classes = [
{ class: GameEnvBackground, data: bgData },
{ class: Player, data: playerData },
];
}
}
export const gameLevelClasses = [CustomLevel];
export { GameControl };
String Operations
- Path concatenation, text display
- Code review: Template literals, concatenation
Level 1: Background Paths
src: path + "/images/gamebuilder/sprites/kirby.png", src: path + "/images/gamebuilder/bg/TimmyGreatBg.png",stepCounterEl.textContent = "Steps: " + steps + " / " + STEP_GOAL;
Boolean Expressions
- Compound conditions in game logic
-
Code review: &&, , !
Level 1/2: Game Logic
if (gameOver && e.keyCode === 82) { steps = 0;```.js if (!this.listenerAdded) { this.listenerAdded = true; // … logic }
// From GameLevelTimmyfuncounter.js if (!musicStarted) { music.play(); musicStarted = true; }
# INPUT/OUTPUT
### Keyboard Input
- Arrow keys, space, WASD controls using event listeners
- Testing: Key event handlers respond correctly
```.js
setTimeout(() => {
playerRef = gameEnv.gameObjects.find(obj => obj.id === 'playerData');
}, 500);
document.addEventListener("keydown", (e) => {
const movementKeys = [87,65,83,68];
if (movementKeys.includes(e.keyCode)) {
steps++;
window.currentSteps = steps;