Back Index Next
Operators Index Documentation

Input / Output

Click a category to see how Heist.exe communicates.


Input / Output in Heist.exe

I/O is how your game receives information and sends it back out. In Heist.exe, the player sends input through the keyboard, the engine receives world config at startup, the music system pulls live data from an API, and the canvas outputs everything the player sees every frame.


flowchart LR
    A[⌨️ WASD Keyboard] --> B[πŸ”„ Game Loop]
    C[βš™οΈ Level Config<br>SCALE_FACTOR,<br>GRAVITY, hitbox] --> B
    D[🌐 iTunes API Response<br>preview track URL] --> B
    B --> E[🎨 Canvas Render<br>Gem.draw / Guard.draw]
    B --> F[🌐 iTunes API Request<br>heistMusic.fetch]

The Four Systems

Keyboard Events β€” Input β€” capture physical key presses and map them to in-game actions

Exactly what it is: The browser detects when the player presses or releases a key, and fires events (keydown, keyup). You listen for these events and respond by updating the player’s state. A key configuration object maps physical key codes to game actions (up, left, down, right).

From Heist.exe:

// HeistL2.js β€” lines 23-24: Player keypress configuration
keypress: { up: 87, left: 65, down: 83, right: 68 }
// W=87, A=65, S=83, D=68 β†’ WASD controls

// Passed as part of sprite_data_mc to HeistPlayer constructor
const sprite_data_mc = {
    id: 'MC',
    name: 'mainplayer',
    src: path + "/images/projects/heist-exe/heist-mc.png",
    SCALE_FACTOR: 10,
    STEP_FACTOR: 500,
    ANIMATION_RATE: 50,
    INIT_POSITION: { x: 0, y: 200 }, 
    keypress: { up: 87, left: 65, down: 83, right: 68 }  // WASD
};

// Game engine listens for keydown/keyup events
// When W (87) is pressed β†’ move player up
// When A (65) is pressed β†’ move player left

GameEnv Config β€” Input β€” system-level settings that configure the world before gameplay

Exactly what it is: Before the game loop starts, you pass a configuration object to every entity. This object specifies spawn position, collision box size, animation speed, physics settings, and more. Changes to this config change behavior without touching any code.

From Heist.exe:

// HeistL2.js β€” sprite_data_guard1 configuration
const sprite_data_guard1 = {
    id: 'Guard1',
    name: 'guard1',
    src: path + "/images/projects/heist-exe/heist-guard.png",
    SCALE_FACTOR: 10,          // Render size: 10x actual sprite pixels
    STEP_FACTOR: 500,          // Movement speed in pixels per second
    ANIMATION_RATE: 50,        // Milliseconds per animation frame
    INIT_POSITION: { x: 220, y: 300 },  // Spawn point
    pixels: { height: 532, width: 400 },  // Sprite sheet dimensions
    hitbox: { widthPercentage: 0.45, heightPercentage: 0.2 }  // Collision box
};

// HeistL3.js (Level 3) example: Enable gravity for different feel
const sprite_data_player = {
    // ... same as above, but also:
    GRAVITY: true  // Enable downward acceleration unique to this level
};

Same Player class, different config = different game feel. HeistL1 might have small hitboxes; HeistL3 adds gravity.


API Calls β€” Input + Output β€” fetch live data from a server and use it in-game

Exactly what it is: Your code sends an HTTP request to an external server (like iTunes Search) asking for data. The server responds with JSON. You parse that JSON, validate it, and use it in your game. This is bidirectional: you output a request, input a response.

From Heist.exe:

// heistMusic.js: Fetch from iTunes Search API
const endpoint = `https://itunes.apple.com/search?term=${encodeURIComponent(picked)}&entity=song&limit=25`;

const response = await fetch(endpoint);  // Output: Send HTTP request
if (!response.ok) {
    throw new Error('API request failed (' + response.status + ')');
}
const data = await response.json();      // Input: Parse response body

// Filter results
let pool = [];
data.results.forEach(track => {
    if (!track.previewUrl) return;       // Must have preview URL
    if (track.artistName.match(VOCAL_HINTS)) return;  // Skip vocal tracks
    pool.push(track);
});

// Pick random valid track
const chosen = pool[Math.floor(Math.random() * pool.length)];
const musicUrl = chosen.previewUrl;      // Use live data from iTunes

The iTunes API returns different results each time, so each game session gets different background music.


Canvas Rendering β€” Output β€” draw everything the player sees every frame

Exactly what it is: You write drawing commands to the canvas context (this.ctx). Each frame, you draw all visible sprites, overlays, and UI elements. If you want something to appear on screen, you have to draw it. If it’s not drawn, it’s not visible.

From Heist.exe:

// Gem.js β€” lines 24-36: draw() method
draw() {
    if (!this.ctx || this.permanentlyCollected) return;  // Don't draw if already collected

    this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);

    if (this.gemImage.complete && this.gemImage.naturalWidth > 0) {
        // Image loaded β€” draw the actual sprite
        this.ctx.drawImage(this.gemImage, 0, 0, this.canvas.width, this.canvas.height);
    } else {
        // Fallback while image loads β€” draw cyan circle so gem is never invisible
        this.ctx.fillStyle = '#00FFFF';
        const cx = this.canvas.width / 2;
        const cy = this.canvas.height / 2;
        const r = Math.min(this.canvas.width, this.canvas.height) / 3;
        this.ctx.beginPath();
        this.ctx.arc(cx, cy, r, 0, Math.PI * 2);
        this.ctx.fill();
    }

    this.setupCanvas();
}

// Guard.js: Drawing happens in update() before collision checks
update() {
    this.draw();  // Render guard sprite to screen
    // ... then check collision
    // ... then move
    // ... then call draw() again next frame
}

If the gem image hasn’t loaded yet, the cyan circle fallback keeps the gem visible instead of disappearing.


The core idea: Input updates state. Output draws that state. The game loop runs every frame as the bridge between the two β€” reading keyboard input, applying physics, then rendering the result.