Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Pixel Rush | Light Trail Platformer</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap'); | |
| :root { | |
| --neon-blue: #00f7ff; | |
| --neon-pink: #ff00f7; | |
| --neon-purple: #9d00ff; | |
| --dark-bg: #0f0a1a; | |
| } | |
| body { | |
| font-family: 'Press Start 2P', cursive; | |
| background-color: var(--dark-bg); | |
| overflow: hidden; | |
| touch-action: none; | |
| user-select: none; | |
| } | |
| .game-container { | |
| position: relative; | |
| width: 100vw; | |
| height: 100vh; | |
| overflow: hidden; | |
| } | |
| #gameCanvas { | |
| background-color: #000; | |
| display: block; | |
| image-rendering: pixelated; | |
| } | |
| .neon-text { | |
| text-shadow: 0 0 10px var(--neon-blue), 0 0 20px var(--neon-pink); | |
| color: white; | |
| } | |
| .neon-border { | |
| box-shadow: 0 0 10px var(--neon-blue), 0 0 20px var(--neon-pink), inset 0 0 10px var(--neon-blue); | |
| border: 2px solid var(--neon-blue); | |
| } | |
| .pixel-button { | |
| transition: all 0.2s; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .pixel-button:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 0 15px var(--neon-blue), 0 0 30px var(--neon-pink); | |
| } | |
| .pixel-button:active { | |
| transform: translateY(1px); | |
| } | |
| .pixel-button::before { | |
| content: ''; | |
| position: absolute; | |
| top: -10px; | |
| left: -10px; | |
| right: -10px; | |
| bottom: -10px; | |
| background: linear-gradient(45deg, var(--neon-blue), var(--neon-pink), var(--neon-purple)); | |
| z-index: -1; | |
| filter: blur(20px); | |
| opacity: 0; | |
| transition: opacity 0.3s; | |
| } | |
| .pixel-button:hover::before { | |
| opacity: 0.7; | |
| } | |
| .glow { | |
| animation: pulse 2s infinite alternate; | |
| } | |
| @keyframes pulse { | |
| 0% { opacity: 0.7; } | |
| 100% { opacity: 1; } | |
| } | |
| .menu-screen { | |
| background: rgba(15, 10, 26, 0.9); | |
| backdrop-filter: blur(5px); | |
| } | |
| .score-display { | |
| background: rgba(0, 0, 0, 0.7); | |
| border: 2px solid var(--neon-blue); | |
| } | |
| .mobile-controls { | |
| touch-action: none; | |
| } | |
| .mobile-btn { | |
| touch-action: none; | |
| background: rgba(0, 0, 0, 0.5); | |
| border: 2px solid var(--neon-blue); | |
| } | |
| .mobile-btn:active { | |
| background: var(--neon-blue); | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="game-container flex items-center justify-center"> | |
| <canvas id="gameCanvas"></canvas> | |
| <!-- Main Menu --> | |
| <div id="mainMenu" class="menu-screen absolute inset-0 flex flex-col items-center justify-center gap-8 neon-text"> | |
| <h1 class="text-5xl md:text-7xl mb-4 glow">PIXEL RUSH</h1> | |
| <div class="flex flex-col gap-4 w-full max-w-md px-4"> | |
| <button id="startBtn" class="pixel-button neon-border bg-black py-4 px-8 text-xl neon-text"> | |
| START GAME | |
| </button> | |
| <button id="howToBtn" class="pixel-button neon-border bg-black py-4 px-8 text-xl neon-text"> | |
| HOW TO PLAY | |
| </button> | |
| <button id="settingsBtn" class="pixel-button neon-border bg-black py-4 px-8 text-xl neon-text"> | |
| SETTINGS | |
| </button> | |
| </div> | |
| <div class="absolute bottom-4 text-sm opacity-70"> | |
| Use arrow keys or touch controls | |
| </div> | |
| </div> | |
| <!-- How To Play --> | |
| <div id="howToMenu" class="menu-screen absolute inset-0 hidden flex-col items-center justify-center gap-6 neon-text p-4"> | |
| <h2 class="text-3xl mb-6">HOW TO PLAY</h2> | |
| <div class="max-w-md bg-black bg-opacity-70 neon-border p-6 text-left text-sm leading-relaxed"> | |
| <div class="mb-4 flex items-start gap-3"> | |
| <i class="fas fa-arrow-up mt-1 text-purple-300"></i> | |
| <p>Press UP to jump over obstacles and gaps</p> | |
| </div> | |
| <div class="mb-4 flex items-start gap-3"> | |
| <i class="fas fa-arrow-left mt-1 text-blue-300"></i> | |
| <i class="fas fa-arrow-right mt-1 text-blue-300"></i> | |
| <p>Move LEFT/RIGHT to navigate platforms</p> | |
| </div> | |
| <div class="mb-4 flex items-start gap-3"> | |
| <i class="fas fa-bolt mt-1 text-yellow-300"></i> | |
| <p>Collect energy orbs to grow your trail</p> | |
| </div> | |
| <div class="mb-4 flex items-start gap-3"> | |
| <i class="fas fa-skull mt-1 text-red-300"></i> | |
| <p>Avoid touching your trail or falling off</p> | |
| </div> | |
| <div class="mt-6 text-center"> | |
| <p class="text-purple-300">The longer your trail, the higher your score!</p> | |
| </div> | |
| </div> | |
| <button id="backFromHowToBtn" class="pixel-button neon-border bg-black py-3 px-6 mt-6 neon-text"> | |
| BACK | |
| </button> | |
| </div> | |
| <!-- Settings --> | |
| <div id="settingsMenu" class="menu-screen absolute inset-0 hidden flex-col items-center justify-center gap-6 neon-text p-4"> | |
| <h2 class="text-3xl mb-6">SETTINGS</h2> | |
| <div class="max-w-md bg-black bg-opacity-70 neon-border p-6 w-full"> | |
| <div class="mb-6"> | |
| <label class="block mb-2">Game Speed</label> | |
| <div class="flex gap-2"> | |
| <button data-speed="0.8" class="speed-btn pixel-button neon-border bg-black py-2 px-4 text-sm active">Slow</button> | |
| <button data-speed="1.2" class="speed-btn pixel-button neon-border bg-black py-2 px-4 text-sm">Normal</button> | |
| <button data-speed="1.6" class="speed-btn pixel-button neon-border bg-black py-2 px-4 text-sm">Fast</button> | |
| </div> | |
| </div> | |
| <div class="mb-6"> | |
| <label class="block mb-2">Trail Style</label> | |
| <div class="flex gap-2 flex-wrap"> | |
| <button data-trail="solid" class="trail-btn pixel-button neon-border bg-black py-2 px-4 text-sm active">Solid</button> | |
| <button data-trail="glow" class="trail-btn pixel-button neon-border bg-black py-2 px-4 text-sm">Glow</button> | |
| <button data-trail="pulse" class="trail-btn pixel-button neon-border bg-black py-2 px-4 text-sm">Pulse</button> | |
| <button data-trail="rainbow" class="trail-btn pixel-button neon-border bg-black py-2 px-4 text-sm">Rainbow</button> | |
| </div> | |
| </div> | |
| <div class="flex items-center justify-between"> | |
| <label for="soundToggle" class="cursor-pointer">Sound Effects</label> | |
| <div class="relative"> | |
| <input type="checkbox" id="soundToggle" class="hidden" checked> | |
| <div class="toggle-bg bg-gray-700 w-12 h-6 rounded-full"> | |
| <div class="toggle-circle absolute left-0 top-0 bg-blue-400 w-6 h-6 rounded-full transition-transform"></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <button id="backFromSettingsBtn" class="pixel-button neon-border bg-black py-3 px-6 mt-6 neon-text"> | |
| BACK | |
| </button> | |
| </div> | |
| <!-- Game Over --> | |
| <div id="gameOverMenu" class="menu-screen absolute inset-0 hidden flex-col items-center justify-center gap-6 neon-text"> | |
| <h2 class="text-4xl text-red-400 mb-2">GAME OVER</h2> | |
| <div class="text-xl mb-2">Your Score:</div> | |
| <div id="finalScore" class="text-5xl text-yellow-300 mb-8">0</div> | |
| <div class="flex flex-col gap-4 w-full max-w-md px-4"> | |
| <button id="restartBtn" class="pixel-button neon-border bg-black py-4 px-8 text-xl neon-text"> | |
| PLAY AGAIN | |
| </button> | |
| <button id="menuBtn" class="pixel-button neon-border bg-black py-4 px-8 text-xl neon-text"> | |
| MAIN MENU | |
| </button> | |
| </div> | |
| </div> | |
| <!-- In-Game UI --> | |
| <div id="gameUI" class="absolute top-0 left-0 right-0 p-4 hidden"> | |
| <div class="flex justify-between items-center"> | |
| <div class="score-display py-2 px-4 rounded"> | |
| <span class="text-yellow-300">SCORE: </span> | |
| <span id="scoreValue" class="text-white">0</span> | |
| </div> | |
| <div class="score-display py-2 px-4 rounded"> | |
| <span class="text-blue-300">LENGTH: </span> | |
| <span id="lengthValue" class="text-white">1</span> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Mobile Controls (hidden by default) --> | |
| <div id="mobileControls" class="absolute bottom-0 left-0 right-0 p-4 hidden mobile-controls"> | |
| <div class="flex justify-between"> | |
| <div class="flex gap-4"> | |
| <button id="mobileLeft" class="mobile-btn w-16 h-16 rounded-full flex items-center justify-center"> | |
| <i class="fas fa-arrow-left text-2xl"></i> | |
| </button> | |
| <button id="mobileRight" class="mobile-btn w-16 h-16 rounded-full flex items-center justify-center"> | |
| <i class="fas fa-arrow-right text-2xl"></i> | |
| </button> | |
| </div> | |
| <button id="mobileUp" class="mobile-btn w-16 h-16 rounded-full flex items-center justify-center"> | |
| <i class="fas fa-arrow-up text-2xl"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Game variables | |
| let canvas, ctx; | |
| let gameWidth, gameHeight; | |
| let gameActive = false; | |
| let score = 0; | |
| let trailLength = 1; | |
| let gameSpeed = 0.8; // Default to slow speed | |
| let trailStyle = 'solid'; | |
| let soundEnabled = true; | |
| let isMobile = false; | |
| // Player variables | |
| let player = { | |
| x: 100, | |
| y: 300, | |
| width: 20, | |
| height: 20, | |
| velocityY: 0, | |
| gravity: 0.3, // Reduced gravity for slower fall | |
| jumpForce: -9, // Reduced jump force for lower jumps | |
| isJumping: false, | |
| speed: 0 | |
| }; | |
| // Trail segments | |
| let trail = []; | |
| const maxTrailLength = 100; | |
| // Platforms | |
| let platforms = []; | |
| const platformHeight = 20; | |
| // Collectibles | |
| let collectibles = []; | |
| // Obstacles | |
| let obstacles = []; | |
| // Initialize game | |
| function init() { | |
| canvas = document.getElementById('gameCanvas'); | |
| ctx = canvas.getContext('2d'); | |
| // Check if mobile | |
| isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); | |
| if (isMobile) { | |
| document.getElementById('mobileControls').classList.remove('hidden'); | |
| } | |
| resizeCanvas(); | |
| setupEventListeners(); | |
| generateInitialPlatforms(); | |
| } | |
| // Resize canvas to window | |
| function resizeCanvas() { | |
| gameWidth = window.innerWidth; | |
| gameHeight = window.innerHeight; | |
| canvas.width = gameWidth; | |
| canvas.height = gameHeight; | |
| // Adjust player position if game is active | |
| if (gameActive) { | |
| player.y = Math.min(player.y, gameHeight - player.height - platformHeight); | |
| } | |
| } | |
| // Set up event listeners | |
| function setupEventListeners() { | |
| // Window resize | |
| window.addEventListener('resize', resizeCanvas); | |
| // Keyboard controls | |
| window.addEventListener('keydown', handleKeyDown); | |
| window.addEventListener('keyup', handleKeyUp); | |
| // Mobile controls | |
| document.getElementById('mobileLeft').addEventListener('touchstart', () => movePlayer('left')); | |
| document.getElementById('mobileLeft').addEventListener('touchend', () => stopPlayer()); | |
| document.getElementById('mobileRight').addEventListener('touchstart', () => movePlayer('right')); | |
| document.getElementById('mobileRight').addEventListener('touchend', () => stopPlayer()); | |
| document.getElementById('mobileUp').addEventListener('touchstart', () => jump()); | |
| // Menu buttons | |
| document.getElementById('startBtn').addEventListener('click', startGame); | |
| document.getElementById('howToBtn').addEventListener('click', () => toggleMenu('howToMenu')); | |
| document.getElementById('settingsBtn').addEventListener('click', () => toggleMenu('settingsMenu')); | |
| document.getElementById('backFromHowToBtn').addEventListener('click', () => toggleMenu('mainMenu')); | |
| document.getElementById('backFromSettingsBtn').addEventListener('click', () => toggleMenu('mainMenu')); | |
| document.getElementById('restartBtn').addEventListener('click', startGame); | |
| document.getElementById('menuBtn').addEventListener('click', () => { | |
| document.getElementById('gameOverMenu').classList.add('hidden'); | |
| document.getElementById('mainMenu').classList.remove('hidden'); | |
| }); | |
| // Settings buttons | |
| document.querySelectorAll('.speed-btn').forEach(btn => { | |
| btn.addEventListener('click', () => { | |
| document.querySelector('.speed-btn.active').classList.remove('active'); | |
| btn.classList.add('active'); | |
| gameSpeed = parseFloat(btn.dataset.speed); | |
| // Update player speed immediately if game is active | |
| if (gameActive) { | |
| player.speed = player.speed > 0 ? 3 * gameSpeed : player.speed < 0 ? -3 * gameSpeed : 0; | |
| } | |
| }); | |
| }); | |
| document.querySelectorAll('.trail-btn').forEach(btn => { | |
| btn.addEventListener('click', () => { | |
| document.querySelector('.trail-btn.active').classList.remove('active'); | |
| btn.classList.add('active'); | |
| trailStyle = btn.dataset.trail; | |
| }); | |
| }); | |
| document.getElementById('soundToggle').addEventListener('change', (e) => { | |
| soundEnabled = e.target.checked; | |
| }); | |
| } | |
| // Toggle between menus | |
| function toggleMenu(menuId) { | |
| document.querySelector('.menu-screen:not(.hidden)').classList.add('hidden'); | |
| document.getElementById(menuId).classList.remove('hidden'); | |
| } | |
| // Handle keyboard input | |
| function handleKeyDown(e) { | |
| if (!gameActive) return; | |
| switch(e.key) { | |
| case 'ArrowLeft': | |
| movePlayer('left'); | |
| break; | |
| case 'ArrowRight': | |
| movePlayer('right'); | |
| break; | |
| case 'ArrowUp': | |
| jump(); | |
| break; | |
| } | |
| } | |
| function handleKeyUp(e) { | |
| if (!gameActive) return; | |
| if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') { | |
| stopPlayer(); | |
| } | |
| } | |
| // Player movement | |
| function movePlayer(direction) { | |
| if (direction === 'left') { | |
| player.speed = -3 * gameSpeed; // Reduced base speed | |
| } else if (direction === 'right') { | |
| player.speed = 3 * gameSpeed; // Reduced base speed | |
| } | |
| } | |
| function stopPlayer() { | |
| player.speed = 0; | |
| } | |
| function jump() { | |
| if (!player.isJumping && gameActive) { | |
| player.velocityY = player.jumpForce; | |
| player.isJumping = true; | |
| } | |
| } | |
| // Start the game | |
| function startGame() { | |
| // Reset game state | |
| score = 0; | |
| trailLength = 1; | |
| trail = []; | |
| platforms = []; | |
| collectibles = []; | |
| obstacles = []; | |
| // Reset player | |
| player = { | |
| x: 100, | |
| y: 300, | |
| width: 20, | |
| height: 20, | |
| velocityY: 0, | |
| gravity: 0.3, | |
| jumpForce: -9, | |
| isJumping: false, | |
| speed: 0 | |
| }; | |
| // Generate initial platforms | |
| generateInitialPlatforms(); | |
| // Hide menus, show game UI | |
| document.querySelector('.menu-screen:not(.hidden)').classList.add('hidden'); | |
| document.getElementById('gameUI').classList.remove('hidden'); | |
| // Start game loop | |
| gameActive = true; | |
| requestAnimationFrame(gameLoop); | |
| } | |
| // Generate initial platforms | |
| function generateInitialPlatforms() { | |
| // Starting platform | |
| platforms.push({ | |
| x: 0, | |
| y: gameHeight - platformHeight, | |
| width: gameWidth, | |
| height: platformHeight | |
| }); | |
| // Additional platforms - spaced further apart for slower gameplay | |
| for (let i = 0; i < 10; i++) { | |
| const width = Math.random() * 150 + 100; // Wider platforms | |
| const x = i * 400 + 250; // More space between platforms | |
| const y = gameHeight - platformHeight - (Math.random() * 150); // Lower height variation | |
| platforms.push({ | |
| x: x, | |
| y: y, | |
| width: width, | |
| height: platformHeight | |
| }); | |
| // Add collectibles above platforms | |
| if (Math.random() > 0.3) { | |
| collectibles.push({ | |
| x: x + width / 2 - 10, | |
| y: y - 30, | |
| width: 20, | |
| height: 20, | |
| collected: false | |
| }); | |
| } | |
| // Add obstacles less frequently | |
| if (Math.random() > 0.8) { | |
| obstacles.push({ | |
| x: x + width / 2 - 15, | |
| y: y - 40, | |
| width: 30, | |
| height: 40 | |
| }); | |
| } | |
| } | |
| } | |
| // Game loop | |
| function gameLoop() { | |
| if (!gameActive) return; | |
| update(); | |
| render(); | |
| requestAnimationFrame(gameLoop); | |
| } | |
| // Update game state | |
| function update() { | |
| // Update player position | |
| player.x += player.speed; | |
| // Apply gravity | |
| player.velocityY += player.gravity; | |
| player.y += player.velocityY; | |
| // Check platform collisions | |
| let onPlatform = false; | |
| for (const platform of platforms) { | |
| if ( | |
| player.x + player.width > platform.x && | |
| player.x < platform.x + platform.width && | |
| player.y + player.height >= platform.y && | |
| player.y + player.height <= platform.y + platform.height / 2 && | |
| player.velocityY > 0 | |
| ) { | |
| player.y = platform.y - player.height; | |
| player.velocityY = 0; | |
| player.isJumping = false; | |
| onPlatform = true; | |
| } | |
| } | |
| // Add current position to trail (less frequently for slower trail growth) | |
| if (trail.length === 0 || | |
| Math.sqrt( | |
| Math.pow(player.x + player.width / 2 - trail[trail.length-1].x, 2) + | |
| Math.pow(player.y + player.height / 2 - trail[trail.length-1].y, 2) | |
| ) > 10) { | |
| trail.push({ | |
| x: player.x + player.width / 2, | |
| y: player.y + player.height / 2 | |
| }); | |
| } | |
| // Limit trail length | |
| if (trail.length > maxTrailLength) { | |
| trail.shift(); | |
| } | |
| // Check collectible collisions | |
| for (const collectible of collectibles) { | |
| if ( | |
| !collectible.collected && | |
| player.x + player.width > collectible.x && | |
| player.x < collectible.x + collectible.width && | |
| player.y + player.height > collectible.y && | |
| player.y < collectible.y + collectible.height | |
| ) { | |
| collectible.collected = true; | |
| score += 10; | |
| trailLength += 3; // Reduced trail growth | |
| } | |
| } | |
| // Check obstacle collisions | |
| for (const obstacle of obstacles) { | |
| if ( | |
| player.x + player.width > obstacle.x && | |
| player.x < obstacle.x + obstacle.width && | |
| player.y + player.height > obstacle.y && | |
| player.y < obstacle.y + obstacle.height | |
| ) { | |
| gameOver(); | |
| return; | |
| } | |
| } | |
| // Check trail collisions (with more lenient collision detection) | |
| for (let i = 0; i < trail.length - 15; i++) { | |
| const segment = trail[i]; | |
| const distance = Math.sqrt( | |
| Math.pow(player.x + player.width / 2 - segment.x, 2) + | |
| Math.pow(player.y + player.height / 2 - segment.y, 2) | |
| ); | |
| if (distance < 12) { // Slightly more lenient collision | |
| gameOver(); | |
| return; | |
| } | |
| } | |
| // Check if player fell off screen | |
| if (player.y > gameHeight) { | |
| gameOver(); | |
| return; | |
| } | |
| // Camera follow - move platforms when player reaches right half | |
| if (player.x > gameWidth / 2) { | |
| const moveAmount = player.x - gameWidth / 2; | |
| player.x = gameWidth / 2; | |
| // Move platforms and collectibles | |
| for (const platform of platforms) { | |
| platform.x -= moveAmount; | |
| } | |
| for (const collectible of collectibles) { | |
| collectible.x -= moveAmount; | |
| } | |
| for (const obstacle of obstacles) { | |
| obstacle.x -= moveAmount; | |
| } | |
| // Move trail segments | |
| for (const segment of trail) { | |
| segment.x -= moveAmount; | |
| } | |
| // Generate new platforms as needed (with more spacing) | |
| if (platforms[platforms.length - 1].x < gameWidth) { | |
| const width = Math.random() * 150 + 100; | |
| const x = platforms[platforms.length - 1].x + 400; | |
| const y = gameHeight - platformHeight - (Math.random() * 150); | |
| platforms.push({ | |
| x: x, | |
| y: y, | |
| width: width, | |
| height: platformHeight | |
| }); | |
| // Add collectibles above platforms | |
| if (Math.random() > 0.3) { | |
| collectibles.push({ | |
| x: x + width / 2 - 10, | |
| y: y - 30, | |
| width: 20, | |
| height: 20, | |
| collected: false | |
| }); | |
| } | |
| // Add obstacles less frequently | |
| if (Math.random() > 0.8) { | |
| obstacles.push({ | |
| x: x + width / 2 - 15, | |
| y: y - 40, | |
| width: 30, | |
| height: 40 | |
| }); | |
| } | |
| } | |
| // Remove off-screen platforms | |
| if (platforms[0].x + platforms[0].width < 0) { | |
| platforms.shift(); | |
| } | |
| // Remove off-screen collectibles | |
| collectibles = collectibles.filter(c => !c.collected && c.x + c.width > 0); | |
| // Remove off-screen obstacles | |
| obstacles = obstacles.filter(o => o.x + o.width > 0); | |
| } | |
| // Update UI | |
| document.getElementById('scoreValue').textContent = score; | |
| document.getElementById('lengthValue').textContent = trailLength; | |
| } | |
| // Render game | |
| function render() { | |
| // Clear canvas | |
| ctx.fillStyle = '#000'; | |
| ctx.fillRect( 0, 0, gameWidth, gameHeight ); | |
| // Draw background (simple starfield) | |
| ctx.fillStyle = '#fff'; | |
| for (let i = 0; i < 100; i++) { | |
| const x = (i * 100 + (Date.now() / 40) % 100) % gameWidth; // Slower star movement | |
| const y = (i * 80) % gameHeight; | |
| const size = Math.random() * 2 + 1; | |
| ctx.fillRect(x, y, size, size); | |
| } | |
| // Draw platforms | |
| ctx.fillStyle = '#444'; | |
| for (const platform of platforms) { | |
| ctx.fillRect(platform.x, platform.y, platform.width, platform.height); | |
| // Platform details | |
| ctx.fillStyle = '#555'; | |
| for (let x = platform.x; x < platform.x + platform.width; x += 20) { | |
| ctx.fillRect(x, platform.y, 15, 3); | |
| } | |
| ctx.fillStyle = '#444'; | |
| } | |
| // Draw collectibles | |
| for (const collectible of collectibles) { | |
| if (!collectible.collected) { | |
| // Glowing effect | |
| ctx.beginPath(); | |
| ctx.arc( | |
| collectible.x + collectible.width / 2, | |
| collectible.y + collectible.height / 2, | |
| collectible.width / 2, | |
| 0, | |
| Math.PI * 2 | |
| ); | |
| const gradient = ctx.createRadialGradient( | |
| collectible.x + collectible.width / 2, | |
| collectible.y + collectible.height / 2, | |
| 0, | |
| collectible.x + collectible.width / 2, | |
| collectible.y + collectible.height / 2, | |
| collectible.width / 2 | |
| ); | |
| gradient.addColorStop(0, 'rgba(255, 255, 0, 0.8)'); | |
| gradient.addColorStop(1, 'rgba(255, 200, 0, 0.2)'); | |
| ctx.fillStyle = gradient; | |
| ctx.fill(); | |
| // Core | |
| ctx.beginPath(); | |
| ctx.arc( | |
| collectible.x + collectible.width / 2, | |
| collectible.y + collectible.height / 2, | |
| collectible.width / 4, | |
| 0, | |
| Math.PI * 2 | |
| ); | |
| ctx.fillStyle = 'yellow'; | |
| ctx.fill(); | |
| } | |
| } | |
| // Draw obstacles | |
| ctx.fillStyle = '#f00'; | |
| for (const obstacle of obstacles) { | |
| // Base | |
| ctx.fillRect(obstacle.x, obstacle.y, obstacle.width, obstacle.height); | |
| // Spikes | |
| ctx.beginPath(); | |
| ctx.moveTo(obstacle.x, obstacle.y); | |
| ctx.lineTo(obstacle.x + obstacle.width / 2, obstacle.y - 15); | |
| ctx.lineTo(obstacle.x + obstacle.width, obstacle.y); | |
| ctx.closePath(); | |
| ctx.fillStyle = '#d00'; | |
| ctx.fill(); | |
| } | |
| // Draw trail | |
| if (trail.length > 1) { | |
| ctx.beginPath(); | |
| ctx.moveTo(trail[0].x, trail[0].y); | |
| for (let i = 1; i < trail.length; i++) { | |
| ctx.lineTo(trail[i].x, trail[i].y); | |
| } | |
| ctx.lineWidth = 8; | |
| switch(trailStyle) { | |
| case 'glow': | |
| ctx.strokeStyle = 'rgba(0, 247, 255, 0.7)'; | |
| ctx.shadowBlur = 15; | |
| ctx.shadowColor = '#00f7ff'; | |
| break; | |
| case 'pulse': | |
| const pulseVal = Math.sin(Date.now() / 300) * 0.3 + 0.7; // Slower pulse | |
| ctx.strokeStyle = `rgba(0, ${Math.floor(247 * pulseVal)}, 255, ${pulseVal})`; | |
| break; | |
| case 'rainbow': | |
| const gradient = ctx.createLinearGradient(0, 0, gameWidth, 0); | |
| gradient.addColorStop(0, '#ff00f7'); | |
| gradient.addColorStop(0.5, '#00f7ff'); | |
| gradient.addColorStop(1, '#9d00ff'); | |
| ctx.strokeStyle = gradient; | |
| break; | |
| default: // solid | |
| ctx.strokeStyle = '#00f7ff'; | |
| } | |
| ctx.stroke(); | |
| // Reset shadow | |
| ctx.shadowBlur = 0; | |
| } | |
| // Draw player | |
| ctx.fillStyle = '#fff'; | |
| ctx.fillRect(player.x, player.y, player.width, player.height); | |
| // Player eyes | |
| ctx.fillStyle = '#00f7ff'; | |
| ctx.fillRect(player.x + 5, player.y + 5, 5, 5); | |
| ctx.fillRect(player.x + 10, player.y + 5, 5, 5); | |
| } | |
| // Game over | |
| function gameOver() { | |
| gameActive = false; | |
| // Show game over menu | |
| document.getElementById('gameUI').classList.add('hidden'); | |
| document.getElementById('gameOverMenu').classList.remove('hidden'); | |
| document.getElementById('finalScore').textContent = score; | |
| } | |
| // Initialize game when page loads | |
| window.addEventListener('load', init); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=sreeramajay/pixel-rush" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |