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);