🚀 Space Adventure Game — CS111 Level Blogs

Explore how we applied CS111 concepts across all three levels of our adventure game:

Level Theme Key Concepts
🌌 Level 1 — Alien Planet Astronaut meets Chill Guy NPC Configuration objects, arrays as registries, && guards
🧩 Level 2 — Alien Maze Invisible maze, find R2 to escape Factory pattern, ?? / ?. operators, fractional coordinates
👾 Level 3 — Alien Chase Survive 20 seconds from the alien Chase AI vector math, concurrent loops, rage mode state machine

Each blog traces every CS111 requirement — iteration, conditionals, data types, and operators — from where we first learned it in Trimester 1 (snake game or calculator) to how we applied it in the level code.

Quick Navigation

   
🚀 Level 1: Space Level 🌀 Level 2: Alien Maze
👾 Level 3: Alien Chase 🎮 Play All Three

About These Levels

Three custom levels were built using the GameEngine framework and exported from GameBuilder. Each level introduces a new mechanic — from basic NPC interaction to invisible maze navigation to a chase-and-survive AI challenge.

All three levels share the same alien planet backdrop and astronaut player sprite, but each one builds on the last with increasing complexity:

# Level Core Mechanic
1 Space Level NPC dialogue + level transition via continue = false
2 Alien Maze Invisible barriers, red glow on collision, timed run
3 Alien Chase Chase AI, survival countdown, rage speed, E-key restart

Use the sections below to read how each level works, then run the embedded game-runner cells to play them directly in the browser.

Quick Controls Reference

Level Move Interact
Space Level ← ↑ → ↓ Arrow Keys Walk into Chill Guy NPC
Alien Maze W A S D Walk to R2, press E
Alien Chase W A S D Press E near alien to restart after being caught

Level 1: Space Level

An introductory level set on an alien planet. Navigate past a set of visible barriers to reach Chill Guy, who will send you to the next level.

How It Works

NPC Interaction and Level Transition

The Npc object (npcData1) overrides two callbacks that the engine calls at specific moments:

  • reaction() — fires every frame the player’s bounding box overlaps the NPC hitbox. Used here to display a dialogue line.
  • interact() — fires when the player presses E within range. The key line is:

Challenge

Navigate the alien planet and interact with Chill Guy to advance to the next level. Use Arrow Keys to move.

Lines: 1 Characters: 0
Game Status: Not Started

Setting currentLevel.continue = false signals the GameControl loop to tear down the current level and advance to the next one in gameLevelClasses.

Barrier Layout

Five Barrier objects are placed using absolute pixel coordinates (x, y, width, height). They are marked visible: true so the player can see and navigate around them. The fromOverlay: true flag marks them as builder-managed, which allows the GameBuilder UI to track and toggle them at edit time.

Challenge

Find R2 hidden in the invisible maze — touch a wall and you restart! Use WASD to move.

Lines: 1 Characters: 0
Game Status: Not Started

Setting hitbox percentages to 0.0 means the engine uses the raw pixel rect for collision — no margin added.

Game Status: Not Started

Level 1 — Key Code Snippets

Player data (arrow key bindings, scale, spawn position):

Challenge

Survive 10 seconds without being caught by the alien slime! Stand still and it goes into rage mode. Use WASD to move, E near the alien to restart.

Lines: 1 Characters: 0
Game Status: Not Started

NPC dialogues array — rotated on each E-key press:

Game Status: Not Started

Level 2: Alien Maze

The walls are invisible. Touch one and you restart from the beginning. Navigate to R2 to complete the level — your time is being tracked!

How It Works

Invisible Barriers with Red Glow on Collision

All barriers are created with visible: false. When a collision is detected, the onCollide callback injects a short-lived <div> over the barrier’s canvas coordinates, styled with a red border and box-shadow. The player is then immediately reset to INIT_POSITION:

Challenge

Play all three levels back to back — Space Level, Alien Maze, then Alien Chase. Use Arrow Keys for Level 1, WASD for Levels 2 and 3.

Lines: 1 Characters: 0
Game Status: Not Started

_glowBarrier() — DOM Overlay Technique

The helper locates the canvas via document.querySelector('canvas'), reads its page offset with getBoundingClientRect(), then positions the glow <div> at canvas.left + barrier.x and canvas.top + barrier.y. A CSS transition: opacity 0.5s fades it out after 300ms, and it is removed from the DOM after 820ms.

Startup Popup and Victory Screen

_showStartupPopup() is called from the constructor before any game objects are created. It appends a dark overlay to document.body with a briefing card. When the player clicks START MISSION, the overlay is removed and GameLevel2._startTime = Date.now() begins the run timer.

When the player reaches R2 and presses E, interact() calls _showVictoryScreen(elapsed) after a short delay. elapsed is computed as:

const elapsed = Math.floor((Date.now() - GameLevel2._startTime) / 1000);

The victory screen formats this as MM:SS and displays it in a green-themed overlay.

Maze Layout

Barrier positions use fractional coordinates (0.0–1.0), which the engine scales to the actual viewport at runtime. The outer walls leave an entrance gap on the left side between y: 0.35 and y: 0.55 — the only way in:

const mazeLeftTop    = makeBarrier('maze_left_top',    0.20, 0.15, 0.02, 0.20);
const mazeLeftBottom = makeBarrier('maze_left_bottom', 0.20, 0.55, 0.02, 0.30);
// gap between y=0.35 and y=0.55 is the entrance
%%js

// GAME_RUNNER: Find R2 hidden in the invisible maze — touch a wall and you restart! Use WASD to move.

import GameControl from '/assets/js/GameEnginev1/essentials/GameControl.js';
import GameLevel2 from '/assets/js/adventureGame/GameLevel2.js';
export const gameLevelClasses = [GameLevel2];
export { GameControl };

Level 2 — Mini-Game (No Code Editor)

Play the maze without the code editor. Good for embedding in lesson articles.

%%js

// GAME_RUNNER: Can you beat the invisible maze? Find R2 to win! | hide_edit: true

import GameControl from '/assets/js/GameEnginev1/essentials/GameControl.js';
import GameLevel2 from '/assets/js/adventureGame/GameLevel2.js';
export const gameLevelClasses = [GameLevel2];
export { GameControl };

Level 2 — Key Code Snippets

Static _startTime — shared across all instances:

/** @static {number|null} Unix timestamp (ms) when the run timer started. */
GameLevel2._startTime = null;
// Set when player dismisses the startup popup:
btn.addEventListener('click', () => {
    overlay.remove();
    GameLevel2._startTime = Date.now();
});

Player finder — searches all common engine object stores:

static _findPlayer(gameEnv) {
    const sources = [
        gameEnv?.gameObjects,
        gameEnv?.objects,
        gameEnv?.gameControl?.gameObjects,
    ].filter(Boolean);
    for (const list of sources) {
        const arr = Array.isArray(list) ? list : Object.values(list);
        const found = arr.find(o => o?.data?.id === 'playerData');
        if (found) return found;
    }
    return null;
}

Level 3: Alien Chase — Survive!

An alien slime is hunting you. Survive for 10 seconds to win. Stand still too long and it goes into rage mode. If it catches you, press E near the alien to restart.

How It Works

This level is the most complex, with two independent systems — AlienChaseAI and SurvivalManager — running alongside the standard game loop.


AlienChaseAI — The Chase Loop

AlienChaseAI runs its own setInterval at TICK_MS: 33 (≈ 30 fps). Each tick it:

  1. Reads the player and NPC canvas positions using getBoundingClientRect().
  2. Computes a normalised direction vector toward the player.
  3. Accumulates an offset (offsetX, offsetY) and applies it to the NPC canvas via CSS:
npcCanvas.style.transform = `translate(${this.offsetX}px, ${this.offsetY}px)`;

Stillness Detection → Rage Mode

Each tick, the AI measures how far the player moved since the last tick. If the movement is below STILL_RADIUS: 4 px, a stillSince timestamp is set. Once Date.now() - stillSince >= STILL_THRESHOLD_MS (3 seconds), the alien switches to RAGE_SPEED: 3.8 px/tick (vs. normal BASE_SPEED: 1.2 px/tick) and the HUD border turns red:

if ((now - this.stillSince) >= CHASE_CONFIG.STILL_THRESHOLD_MS) {
    this.currentSpeed = CHASE_CONFIG.RAGE_SPEED;
    this._setRageVisual(true);   // HUD pulses red
}

As soon as the player moves again, speed resets to BASE_SPEED and the HUD reverts to teal.


SurvivalManager — The Countdown

SurvivalManager runs a separate setInterval every 100ms, ticking down remaining from 10000ms. The HUD displays tenths of a second in the final 3 seconds for extra tension. When remaining reaches zero, showWin() fires and the alien freezes.


Full Interaction Chain

Player moves into Alien hitbox
  └─ Engine calls NPC.reaction()
       └─ SurvivalManager.caught()
            ├─ frozen = true
            ├─ countdown stopped
            ├─ CAUGHT overlay → display: block
            └─ AlienChaseAI.pause()

Player presses E near Alien
  └─ Engine calls NPC.interact()
       └─ SurvivalManager.reset()
            ├─ remaining = 10000ms
            ├─ frozen = false
            ├─ CAUGHT/WIN overlays → display: none
            ├─ AlienChaseAI.resume()  (alien teleports to spawn)
            └─ countdown restarted

Countdown reaches zero
  └─ SurvivalManager.showWin()
       ├─ frozen = true
       ├─ WIN overlay → display: block
       ├─ AlienChaseAI.pause()
       └─ (after 4s) currentLevel.continue = false  → next level

Catch Cooldown — Grace Period After Restart

On restart, lastResetTime = Date.now() is recorded. caught() will not fire again until CATCH_COOLDOWN_MS: 1200 ms have passed — giving the player a brief grace window to move away from the spawn point before the alien can catch them again.


Barrier — Blocking the Top-Left Corner

A single Barrier at (0, 1) with size 235×134px blocks the top-left escape route, funneling the player toward the alien and increasing collision likelihood. This is an intentional level-design choice to raise tension.

%%js

// GAME_RUNNER: Survive 10 seconds without being caught by the alien slime! Stand still and it goes into rage mode. Use WASD to move, E near the alien to restart.

import GameControl from '/assets/js/GameEnginev1/essentials/GameControl.js';
import GameLevelstuck_final from '/assets/js/adventureGame/GameLevelstuck_final.js';
export const gameLevelClasses = [GameLevelstuck_final];
export { GameControl };

Level 3 — Mini-Game (No Code Editor)

Play the survival challenge without the code editor.

%%js

// GAME_RUNNER: Survive the alien chase! Can you last 10 seconds? | hide_edit: true

import GameControl from '/assets/js/GameEnginev1/essentials/GameControl.js';
import GameLevelstuck_final from '/assets/js/adventureGame/GameLevelstuck_final.js';
export const gameLevelClasses = [GameLevelstuck_final];
export { GameControl };

Level 3 — Key Code Snippets

CHASE_CONFIG — all AI tuning in one place:

const CHASE_CONFIG = {
    TICK_MS:            33,     // ~30 fps
    BASE_SPEED:         1.2,    // px/tick — normal
    RAGE_SPEED:         3.8,    // px/tick — when player stands still
    STILL_THRESHOLD_MS: 3000,   // ms of stillness to trigger rage
    STILL_RADIUS:       4,      // px — movement below this = "still"
    STOP_RADIUS:        18,     // px — alien stops nudging this close
    INIT_POSITION:      { x: 500, y: 300 }
};

HUD last-3-second warning:

_updateHUD() {
    const secs = this.remaining / 1000;
    // Show tenths in the final 3 seconds
    const display = secs <= 3 ? `⏱ ${secs.toFixed(1)}s` : `⏱ ${Math.ceil(secs)}s`;
    el.textContent = display;
    if (secs <= 3) {
        el.style.color = '#ff9900'; // orange pulse
    }
}

Playing All Three Levels in Sequence

The levels were designed to chain together. Pass all three classes to gameLevelClasses and the engine will advance through them automatically as each level’s continue flag is set to false.

%%js

// GAME_RUNNER: Play all three levels back to back — Space Level, Alien Maze, then Alien Chase. Use Arrow Keys for Level 1, WASD for Levels 2 and 3.

import GameControl from '/assets/js/GameEnginev1/essentials/GameControl.js';
import GameLevelSpacelevel3 from '/assets/js/adventureGame/GameLevelSpacelevel3.js';
import GameLevel2 from '/assets/js/adventureGame/GameLevel2.js';
import GameLevelstuck_final from '/assets/js/adventureGame/GameLevelstuck_final.js';
export const gameLevelClasses = [GameLevelSpacelevel3, GameLevel2, GameLevelstuck_final];
export { GameControl };

Image Asset Reference

All levels resolve assets via gameEnv.path. Ensure the following files exist in your repo before publishing:

Asset Path
Alien planet background images/gamebuilder/bg/alien_planet.jpg
Astronaut player sprite images/gamebuilder/sprites/astro.png
Chill Guy NPC (Level 1) images/gamify/chillguy.png
R2 robot NPC (Level 2) images/gamify/r2_idle.png
Alien slime NPC (Level 3) images/gamebuilder/sprites/slime.png

Build and Test

After adding or editing this notebook, run make to convert it to an interactive HTML page:

make
# Then open: http://localhost:4000/adventure-game-levels

Checklist before publishing:

  • Each game-runner cell renders a canvas with Play / Stop / Reset controls.
  • Level transitions work (NPC interact or ESC key advances to next level).
  • Level 2 startup popup and victory screen appear and close correctly.
  • Level 3 HUD timer counts down; CAUGHT and WIN overlays display correctly.
  • hide_edit: true cells hide the code editor and show only the game canvas.
  • All five image assets resolve without 404 errors (check browser console).