diff options
Diffstat (limited to 'html/rogue/js/rogue.js')
-rw-r--r-- | html/rogue/js/rogue.js | 134 |
1 files changed, 42 insertions, 92 deletions
diff --git a/html/rogue/js/rogue.js b/html/rogue/js/rogue.js index b4379ea..75a4c86 100644 --- a/html/rogue/js/rogue.js +++ b/html/rogue/js/rogue.js @@ -3,11 +3,20 @@ window.gameState = null; const initGame = () => { const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); + + // Target frame rate + const FPS = 60; + const FRAME_TIME = 1000 / FPS; + let lastFrameTime = 0; // Set canvas to full viewport size const resizeCanvas = () => { canvas.width = window.innerWidth; canvas.height = window.innerHeight; + // Clear any cached gradients when resizing + if (window.gameState) { + window.gameState.cachedGradient = null; + } }; window.addEventListener('resize', resizeCanvas); @@ -16,7 +25,7 @@ const initGame = () => { // Game state let gameState = { time: 0, - player: createPlayer(100, 100), // Starting position + player: createPlayer(100, 100), camera: createCamera(0, 0), world: createWorld(), debug: { @@ -29,42 +38,37 @@ const initGame = () => { // Make gameState globally accessible window.gameState = gameState; - // Throttle mouse move updates - let mouseMoveThrottle; - canvas.addEventListener('mousemove', (e) => { - if (!mouseMoveThrottle) { - mouseMoveThrottle = setTimeout(() => { - gameState.debug.mouseX = e.clientX + gameState.camera.x; - gameState.debug.mouseY = e.clientY + gameState.camera.y; - mouseMoveThrottle = null; - }, 16); // ~60fps - } - }); - - // Add debug toggle listener - window.addEventListener('keydown', (e) => { - if (e.key === 'd') { - gameState.debug.enabled = !gameState.debug.enabled; - } - }); + // Set up input handlers + setupInputHandlers(canvas, gameState); // Game loop const gameLoop = (timestamp) => { - // Calculate delta time - const deltaTime = timestamp - gameState.time; + // Check if enough time has passed since last frame + if (timestamp - lastFrameTime < FRAME_TIME) { + requestAnimationFrame(gameLoop); + return; + } + + // Calculate delta time (capped at 1 second to prevent huge jumps) + const deltaTime = Math.min(timestamp - lastFrameTime, 1000); + lastFrameTime = timestamp; gameState.time = timestamp; + // Clear the entire canvas + ctx.clearRect(0, 0, canvas.width, canvas.height); + // Update gameState = updateGame(gameState, deltaTime); // Render render(ctx, gameState); - // Next frame + // Schedule next frame requestAnimationFrame(gameLoop); }; // Start the game loop + lastFrameTime = performance.now(); requestAnimationFrame(gameLoop); }; @@ -80,107 +84,53 @@ const updateGame = (state, deltaTime) => { }; const render = (ctx, state) => { - // Calculate groundY once at the start const groundY = ctx.canvas.height - state.world.groundHeight; - // Create gradient for sky - cache this instead of recreating every frame + // Cache sky gradient if (!state.cachedGradient) { - const gradient = ctx.createLinearGradient(0, 0, 0, groundY); - gradient.addColorStop(0, '#1a1a2e'); - gradient.addColorStop(0.4, '#2d1b3d'); - gradient.addColorStop(0.7, '#462639'); - gradient.addColorStop(1, '#1f1f2f'); - state.cachedGradient = gradient; + state.cachedGradient = createSkyGradient(ctx, groundY); } - // Fill sky using cached gradient - ctx.fillStyle = state.cachedGradient; - ctx.fillRect(0, 0, ctx.canvas.width, groundY); - - // Implement view frustum culling - only render visible objects - const viewBounds = { - left: state.camera.x - 100, // Add small buffer - right: state.camera.x + state.camera.width + 100, - top: state.camera.y - 100, - bottom: state.camera.y + state.camera.height + 100 - }; + const viewBounds = getViewBounds(state.camera); // Apply camera transform ctx.save(); ctx.translate(-state.camera.x, -state.camera.y); - // Fill black background - ctx.fillStyle = '#000000'; - ctx.fillRect( + // Clear the transformed canvas area + ctx.clearRect( viewBounds.left, - groundY, + 0, viewBounds.right - viewBounds.left, ctx.canvas.height ); - // 1. Render only visible background objects - state.world.backgroundTrees.forEach(tree => { - if (tree.x > viewBounds.left && tree.x < viewBounds.right) { - renderTree(ctx, tree, groundY); - } - }); + // Render layers + renderBackground(ctx, state, groundY, viewBounds); + renderBackgroundObjects(ctx, state, groundY, viewBounds); - state.world.backgroundRocks.forEach(rock => { - if (rock.x > viewBounds.left && rock.x < viewBounds.right) { - renderRock(ctx, rock, groundY); - } - }); - - state.world.backgroundGrass.forEach(grass => { - if (grass.x > viewBounds.left && grass.x < viewBounds.right) { - renderGrass(ctx, grass, groundY, state.time); - } - }); - - // 2. Render platforms + // Render platforms state.world.platforms.forEach(platform => { ctx.fillStyle = platform.color; ctx.fillRect(platform.x, platform.y, platform.width, platform.height); }); - // 3. Render player renderPlayer(ctx, state.player); + renderForegroundObjects(ctx, state, groundY, viewBounds); - // 4. Render foreground objects - state.world.foregroundTrees.forEach(tree => { - if (tree.x > viewBounds.left && tree.x < viewBounds.right) { - renderTree(ctx, tree, groundY); - } - }); - - state.world.foregroundRocks.forEach(rock => { - if (rock.x > viewBounds.left && rock.x < viewBounds.right) { - renderRock(ctx, rock, groundY); - } - }); - - state.world.foregroundGrass.forEach(grass => { - if (grass.x > viewBounds.left && grass.x < viewBounds.right) { - renderGrass(ctx, grass, groundY, state.time); - } - }); - - // 5. Render ground (just the grass top) - ctx.fillStyle = '#4a4'; + // Render ground line + ctx.fillStyle = RENDER_CONSTANTS.GROUND_COLOR; ctx.fillRect( state.camera.x - 1000, groundY, ctx.canvas.width + 2000, - 1 // Only render the grass line + 1 ); - // Render debug information if enabled + // Handle debug rendering if (state.debug.enabled) { ctx.restore(); - ctx.fillStyle = '#ffffff'; - ctx.font = '14px monospace'; - const text = `x: ${Math.round(state.debug.mouseX)}, y: ${Math.round(state.debug.mouseY)}`; - ctx.fillText(text, 10, ctx.canvas.height - 20); + renderDebugInfo(ctx, state); } else { ctx.restore(); } |