From 5b1d0affca49bae5d60b954a59bf229d78643b38 Mon Sep 17 00:00:00 2001 From: OpenClaw Engineer Date: Tue, 3 Mar 2026 20:39:28 -0600 Subject: [PATCH] feat: add TRex movement controls, jump/duck, and forward-step scoring --- js/game.js | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 2 deletions(-) diff --git a/js/game.js b/js/game.js index e33ce23..b248ca1 100644 --- a/js/game.js +++ b/js/game.js @@ -1,14 +1,97 @@ const canvas = document.getElementById('game'); const ctx = canvas.getContext('2d'); +const scoreEl = document.getElementById('score'); + +const keys = new Set(); +const gravity = 1900; +const groundY = 420; const state = { running: true, - lastTs: performance.now() + lastTs: performance.now(), + score: 0, + stepCarry: 0, + player: { + x: 160, + y: 0, + width: 64, + height: 76, + standingHeight: 76, + duckHeight: 48, + vx: 0, + vy: 0, + speed: 300, + jumpPower: 760, + onGround: true, + ducking: false + } }; +state.player.y = groundY - state.player.height; + +window.addEventListener('keydown', (e) => { + if (["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(e.key)) { + e.preventDefault(); + } + keys.add(e.key); + + if (e.key === 'ArrowUp' && state.player.onGround) { + state.player.vy = -state.player.jumpPower; + state.player.onGround = false; + } +}); +window.addEventListener('keyup', (e) => keys.delete(e.key)); function drawBackground() { + ctx.fillStyle = '#8ddcff'; + ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = '#88d34f'; - ctx.fillRect(0, 420, canvas.width, 120); + ctx.fillRect(0, groundY, canvas.width, canvas.height - groundY); +} + +function drawTRex(p) { + ctx.fillStyle = '#35535f'; + ctx.fillRect(p.x, p.y, p.width, p.height); + ctx.fillStyle = '#27404a'; + ctx.fillRect(p.x + p.width - 12, p.y + 14, 7, 7); // eye + ctx.fillRect(p.x + 10, p.y + p.height - 8, 14, 8); // foot1 + ctx.fillRect(p.x + p.width - 24, p.y + p.height - 8, 14, 8); // foot2 +} + +function updatePlayer(dt) { + const p = state.player; + let move = 0; + + if (keys.has('ArrowLeft')) move -= 1; + if (keys.has('ArrowRight')) move += 1; + + p.ducking = keys.has('ArrowDown') && p.onGround; + const targetHeight = p.ducking ? p.duckHeight : p.standingHeight; + if (targetHeight !== p.height) { + p.y += p.height - targetHeight; + p.height = targetHeight; + } + + p.vx = move * p.speed; + const oldX = p.x; + p.x += p.vx * dt; + p.x = Math.max(0, Math.min(canvas.width - p.width, p.x)); + + if (p.x > oldX) { + state.stepCarry += p.x - oldX; + while (state.stepCarry >= 8) { + state.score += 1; + state.stepCarry -= 8; + } + } + + p.vy += gravity * dt; + p.y += p.vy * dt; + + if (p.y + p.height >= groundY) { + p.y = groundY - p.height; + p.vy = 0; + p.onGround = true; + } } function tick(ts) { @@ -16,8 +99,13 @@ function tick(ts) { const dt = Math.min((ts - state.lastTs) / 1000, 0.05); state.lastTs = ts; + updatePlayer(dt); + ctx.clearRect(0, 0, canvas.width, canvas.height); drawBackground(); + drawTRex(state.player); + + scoreEl.textContent = `Score: ${state.score}`; requestAnimationFrame(tick); }