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