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); resizeCanvas(); // Calculate initial player position at ground level const groundY = canvas.height - CONFIG.world.groundHeight; const initialPlayerY = groundY - CONFIG.player.height; // Game state let gameState = { time: 0, player: createPlayer(100, initialPlayerY), camera: createCamera(0, 0), world: createWorld(), debug: { enabled: false, mouseX: 0, mouseY: 0 } }; // Make gameState globally accessible window.gameState = gameState; // Set up input handlers setupInputHandlers(canvas, gameState); // Game loop const gameLoop = (timestamp) => { // 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); // Schedule next frame requestAnimationFrame(gameLoop); }; // Start the game loop lastFrameTime = performance.now(); requestAnimationFrame(gameLoop); }; const updateGame = (state, deltaTime) => { const updatedPlayer = updatePlayer(state.player, deltaTime); const updatedCamera = updateCamera(state.camera, updatedPlayer); return { ...state, player: updatedPlayer, camera: updatedCamera }; }; const render = (ctx, state) => { const groundY = ctx.canvas.height - state.world.groundHeight; // Cache sky gradient if (!state.cachedGradient) { state.cachedGradient = createSkyGradient(ctx, groundY); } const viewBounds = getViewBounds(state.camera); // Apply camera transform ctx.save(); ctx.translate(-state.camera.x, -state.camera.y); // Clear the transformed canvas area ctx.clearRect( viewBounds.left, 0, viewBounds.right - viewBounds.left, ctx.canvas.height ); // Render layers renderBackground(ctx, state, groundY, viewBounds); renderBackgroundObjects(ctx, state, groundY, viewBounds); // Render platforms state.world.platforms.forEach(platform => { ctx.fillStyle = platform.color; ctx.fillRect(platform.x, platform.y, platform.width, platform.height); }); renderPlayer(ctx, state.player); renderForegroundObjects(ctx, state, groundY, viewBounds); // Render ground line ctx.fillStyle = RENDER_CONSTANTS.GROUND_COLOR; ctx.fillRect( state.camera.x - 1000, groundY, ctx.canvas.width + 2000, 1 ); // Handle debug rendering if (state.debug.enabled) { ctx.restore(); renderDebugInfo(ctx, state); } else { ctx.restore(); } }; // Initialize the game when the window loads window.addEventListener('load', initGame);