import game from "/data.js"; // import the game data // import elements from html const canvas = document.getElementById("canvas"); const ctx = canvas.getContext("2d"); // resize the canvas to full screen function resize() { const w = window.innerWidth; const h = window.innerHeight; canvas.width = w; canvas.height = h; ctx.width = w; ctx.height = h; game.size = [w, h]; } // reset the player to the last checkpoint (or start) function die() { game.player.x = game.player.savex; game.player.y = game.player.savey; game.player.velx = 0; game.player.vely = 0; } function draw() { if (!game.cache.dirty) return; requestAnimationFrame(draw); // to continuously animate const offx = game.camera[0] - game.size[0] / 2; const offy = game.camera[1] - game.size[1] / 2; const c = game.color; const point = (x, y) => [x - offx, y - offy]; const color = (which) => (ctx.fillStyle = game.color[which]); const rect = (x, y, w, h) => ctx.fillRect(...point(x, y), w, h); // draw background color("background"); ctx.fillRect(0, 0, game.size[0], game.size[1]); color("dots"); for (let i = 0; i < 30; i++) { for (let j = 0; j < 40; j++) { const spacing = 60; const depth = 0.5; ctx.beginPath(); ctx.arc( i * spacing - (offx % (spacing / depth)) * depth, j * spacing - (offy % (spacing / depth)) * depth, 5, 0, 2 * Math.PI ); ctx.fill(); } } // draw checkpoint animation (if there is one) if (game.anim[2] < 1) { color("shadow"); ctx.globalAlpha = 1 - game.anim[2]; ctx.beginPath(); ctx.arc( ...point(game.anim[0], game.anim[1]), game.anim[2] * 100 + 25, 0, 2 * Math.PI ); ctx.fill(); ctx.globalAlpha = 1; } // draw platforms for (let part of game.parts) { color("shadow"); for (let i = 6; i > 0; i--) { rect(part[0] + i, part[1] + i, part[2], part[3]); } color(part[4] || "platforms"); rect(part[0], part[1], part[2], part[3]); } // draw player color("player"); rect(game.player.x, game.player.y, game.player.size, game.player.size); game.cache.dirty = true; } function update() { const p = game.player; const c = game.camera; // handle input const i = game.input; const speed = 2; if (i.up && p.canJump) p.vely = -14; if (i.down) p.vely += 0.8; if (i.left) p.velx -= speed; if (i.right) p.velx += speed; // update x position for (let i = 0; i < Math.abs(p.velx) - 1; i++) { p.x += p.velx < 0 ? -1 : 1; const c = collision(); if (c) { p.x -= p.velx < 0 ? -1 : 1; p.velx = 0; } } // reset jump if they're falling or already jumping (there's a small grace period though) if (Math.abs(p.vely) > 3) p.canJump = false; // update y position const collided = move("y", p.vely, p.vely < 0 ? -1 : 1); if (collided) { if (p.vely >= 0) { // if they fell onto a platform then let them jump p.canJump = true; // if they hit a checkpoint if (collided[4] === "checkpoint" || collided[4] === "final") { // unset the other checkpoints for (let part of game.parts.filter((i) => i[4] === "active")) { part[4] = "checkpoint"; } const x = collided[0] + collided[2] / 2; const y = collided[1] + collided[3] / 2; // play the checkpoint animation game.anim = [x, y, 0]; game.player.savex = x - game.player.size / 2; game.player.savey = y - collided[3] / 2 - game.player.size; // they won! if (collided[4] === "final") { alert("ayo you win"); // reset the inputs, `alert()` does weird things input.up = false; input.down = false; input.left = false; input.right = false; } // don't spam animation; change platform color collided[4] = collided[4] === "final" ? "win" : "active"; } } p.vely = 0; } // they fell out of the world if (p.y > 1000) die(); // update velocity p.velx *= 0.8; p.vely = Math.min(30, p.vely + 0.8); // update camera const camx = (c[0] * 9 + p.x) / 10; const camy = (c[1] * 9 + p.y) / 10; if (c[0] !== camx || c[1] !== camy) game.cache.dirty = true; c[0] = camx; c[1] = camy; if (game.anim[2] < 1) game.anim[2] += 0.04; // move, and return the platform the player collided with if any function move(key, amt, speed) { for (let i = 0; i < Math.abs(amt) - 1; i++) { p[key] += speed; const c = collision(); if (c) { p[key] -= speed; return c; } } return null; } // check if there was a collision function collision() { const s = game.player.size; for (let part of game.parts) { const inside = p.x + s > part[0] && p.y + s > part[1] && p.x < part[0] + part[2] && p.y < part[1] + part[3]; if (inside) return part; } return false; } } // handle input function input(e) { const i = game.input; const down = e.type === "keydown"; switch (e.code) { case "ArrowUp": case "KeyW": return (i.up = down); case "ArrowDown": case "KeyS": return (i.down = down); case "ArrowLeft": case "KeyA": return (i.left = down); case "ArrowRight": case "KeyD": return (i.right = down); case "KeyR": return die(); } } // run the game! resize(); draw(); setInterval(update, 1000 / 60); window.addEventListener("resize", resize); window.addEventListener("keydown", input); window.addEventListener("keyup", input);