Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Video Backdrop Game</title> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| overflow: hidden; | |
| background: #000; | |
| } | |
| #gameContainer { | |
| position: relative; | |
| width: 100vw; | |
| height: 100vh; | |
| overflow: hidden; | |
| } | |
| #videoBackground { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| object-fit: cover; | |
| z-index: -1; | |
| } | |
| #gameCanvas { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| z-index: 1; | |
| } | |
| #ui { | |
| position: absolute; | |
| top: 20px; | |
| left: 20px; | |
| z-index: 2; | |
| color: white; | |
| font-size: 24px; | |
| text-shadow: 2px 2px 4px rgba(0,0,0,0.8); | |
| font-weight: bold; | |
| } | |
| #startScreen, #gameOverScreen { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: rgba(0, 0, 0, 0.7); | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: center; | |
| align-items: center; | |
| z-index: 3; | |
| color: white; | |
| text-align: center; | |
| } | |
| #startScreen h1, #gameOverScreen h1 { | |
| font-size: 48px; | |
| margin-bottom: 20px; | |
| text-shadow: 3px 3px 6px rgba(0,0,0,0.9); | |
| } | |
| #startScreen p, #gameOverScreen p { | |
| font-size: 20px; | |
| margin-bottom: 30px; | |
| max-width: 600px; | |
| line-height: 1.5; | |
| } | |
| button { | |
| padding: 15px 40px; | |
| font-size: 24px; | |
| background: linear-gradient(45deg, #ff6b6b, #4ecdc4); | |
| color: white; | |
| border: none; | |
| border-radius: 50px; | |
| cursor: pointer; | |
| transition: transform 0.2s, box-shadow 0.2s; | |
| font-weight: bold; | |
| text-transform: uppercase; | |
| } | |
| button:hover { | |
| transform: scale(1.05); | |
| box-shadow: 0 5px 15px rgba(0,0,0,0.3); | |
| } | |
| button:active { | |
| transform: scale(0.95); | |
| } | |
| #finalScore { | |
| color: #4ecdc4; | |
| font-size: 36px; | |
| margin: 20px 0; | |
| } | |
| .hidden { | |
| display: none ; | |
| } | |
| #instructions { | |
| margin-top: 20px; | |
| font-size: 16px; | |
| opacity: 0.8; | |
| } | |
| #builtWith { | |
| position: absolute; | |
| bottom: 20px; | |
| right: 20px; | |
| z-index: 2; | |
| color: white; | |
| font-size: 14px; | |
| text-shadow: 1px 1px 2px rgba(0,0,0,0.8); | |
| } | |
| #builtWith a { | |
| color: #4ecdc4; | |
| text-decoration: none; | |
| font-weight: bold; | |
| } | |
| #builtWith a:hover { | |
| text-decoration: underline; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="gameContainer"> | |
| <video id="videoBackground" autoplay muted loop> | |
| <source src="uploads/927c991d-45a6-46cf-91ee-4d13ba8476ea.mp4" type="video/mp4"> | |
| Your browser does not support the video tag. | |
| </video> | |
| <canvas id="gameCanvas"></canvas> | |
| <div id="ui"> | |
| <div>Score: <span id="score">0</span></div> | |
| <div>Lives: <span id="lives">3</span></div> | |
| </div> | |
| <div id="builtWith"> | |
| Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">anycoder</a> | |
| </div> | |
| <div id="startScreen"> | |
| <h1>Space Defender</h1> | |
| <p>Defend your space station from incoming asteroids! Use your mouse or finger to move the spaceship.</p> | |
| <button id="startButton">Start Game</button> | |
| <div id="instructions"> | |
| <p>Move: Mouse/Touch | Auto-fire enabled</p> | |
| </div> | |
| </div> | |
| <div id="gameOverScreen" class="hidden"> | |
| <h1>Game Over</h1> | |
| <p id="finalScore">Final Score: 0</p> | |
| <p id="gameOverMessage"></p> | |
| <button id="restartButton">Play Again</button> | |
| </div> | |
| </div> | |
| <script> | |
| const canvas = document.getElementById('gameCanvas'); | |
| const ctx = canvas.getContext('2d'); | |
| const video = document.getElementById('videoBackground'); | |
| const startScreen = document.getElementById('startScreen'); | |
| const gameOverScreen = document.getElementById('gameOverScreen'); | |
| const startButton = document.getElementById('startButton'); | |
| const restartButton = document.getElementById('restartButton'); | |
| const scoreElement = document.getElementById('score'); | |
| const livesElement = document.getElementById('lives'); | |
| const finalScoreElement = document.getElementById('finalScore'); | |
| const gameOverMessageElement = document.getElementById('gameOverMessage'); | |
| // Set canvas size | |
| function resizeCanvas() { | |
| canvas.width = window.innerWidth; | |
| canvas.height = window.innerHeight; | |
| } | |
| resizeCanvas(); | |
| window.addEventListener('resize', resizeCanvas); | |
| // Game variables | |
| let gameRunning = false; | |
| let score = 0; | |
| let lives = 3; | |
| let mouseX = canvas.width / 2; | |
| let mouseY = canvas.height - 100; | |
| // Player spaceship | |
| const player = { | |
| x: canvas.width / 2, | |
| y: canvas.height - 100, | |
| width: 60, | |
| height: 60, | |
| speed: 8 | |
| }; | |
| // Game objects arrays | |
| let asteroids = []; | |
| let bullets = []; | |
| let particles = []; | |
| // Input handling | |
| let isMouseDown = false; | |
| canvas.addEventListener('mousemove', (e) => { | |
| const rect = canvas.getBoundingClientRect(); | |
| mouseX = e.clientX - rect.left; | |
| mouseY = e.clientY - rect.top; | |
| }); | |
| canvas.addEventListener('touchmove', (e) => { | |
| e.preventDefault(); | |
| const rect = canvas.getBoundingClientRect(); | |
| mouseX = e.touches[0].clientX - rect.left; | |
| mouseY = e.touches[0].clientY - rect.top; | |
| }); | |
| canvas.addEventListener('mousedown', () => isMouseDown = true); | |
| canvas.addEventListener('mouseup', () => isMouseDown = false); | |
| canvas.addEventListener('touchstart', (e) => { | |
| e.preventDefault(); | |
| isMouseDown = true; | |
| const rect = canvas.getBoundingClientRect(); | |
| mouseX = e.touches[0].clientX - rect.left; | |
| mouseY = e.touches[0].clientY - rect.top; | |
| }); | |
| canvas.addEventListener('touchend', () => isMouseDown = false); | |
| // Asteroid class | |
| class Asteroid { | |
| constructor() { | |
| this.width = 30 + Math.random() * 40; | |
| this.height = this.width; | |
| this.x = Math.random() * canvas.width; | |
| this.y = -this.height; | |
| this.speed = 2 + Math.random() * 4; | |
| this.rotation = 0; | |
| this.rotationSpeed = (Math.random() - 0.5) * 0.1; | |
| } | |
| update() { | |
| this.y += this.speed; | |
| this.rotation += this.rotationSpeed; | |
| } | |
| draw() { | |
| ctx.save(); | |
| ctx.translate(this.x + this.width / 2, this.y + this.height / 2); | |
| ctx.rotate(this.rotation); | |
| ctx.strokeStyle = '#ff6b6b'; | |
| ctx.lineWidth = 2; | |
| ctx.beginPath(); | |
| ctx.arc(0, 0, this.width / 2, 0, Math.PI * 2); | |
| ctx.stroke(); | |
| ctx.restore(); | |
| } | |
| isOffScreen() { | |
| return this.y > canvas.height; | |
| } | |
| collidesWith(obj) { | |
| return this.x < obj.x + obj.width && | |
| this.x + this.width > obj.x && | |
| this.y < obj.y + obj.height && | |
| this.y + this.height > obj.y; | |
| } | |
| } | |
| // Bullet class | |
| class Bullet { | |
| constructor(x, y) { | |
| this.x = x; | |
| this.y = y; | |
| this.width = 4; | |
| this.height = 15; | |
| this.speed = 12; | |
| } | |
| update() { | |
| this.y -= this.speed; | |
| } | |
| draw() { | |
| ctx.fillStyle = '#4ecdc4'; | |
| ctx.fillRect(this.x - this.width / 2, this.y, this.width, this.height); | |
| // Add glow effect | |
| ctx.shadowBlur = 10; | |
| ctx.shadowColor = '#4ecdc4'; | |
| ctx.fillRect(this.x - this.width / 2, this.y, this.width, this.height); | |
| ctx.shadowBlur = 0; | |
| } | |
| isOffScreen() { | |
| return this.y < -this.height; | |
| } | |
| } | |
| // Particle class for explosions | |
| class Particle { | |
| constructor(x, y, color) { | |
| this.x = x; | |
| this.y = y; | |
| this.vx = (Math.random() - 0.5) * 8; | |
| this.vy = (Math.random() - 0.5) * 8; | |
| this.life = 1; | |
| this.decay = 0.02; | |
| this.color = color; | |
| this.size = Math.random() * 4 + 2; | |
| } | |
| update() { | |
| this.x += this.vx; | |
| this.y += this.vy; | |
| this.life -= this.decay; | |
| this.vx *= 0.98; | |
| this.vy *= 0.98; | |
| } | |
| draw() { | |
| ctx.save(); | |
| ctx.globalAlpha = this.life; | |
| ctx.fillStyle = this.color; | |
| ctx.fillRect(this.x, this.y, this.size, this.size); | |
| ctx.restore(); | |
| } | |
| isDead() { | |
| return this.life <= 0; | |
| } | |
| } | |
| // Create explosion effect | |
| function createExplosion(x, y, color) { | |
| for (let i = 0; i < 20; i++) { | |
| particles.push(new Particle(x, y, color)); | |
| } | |
| } | |
| // Spawn asteroids | |
| let asteroidSpawnTimer = 0; | |
| function spawnAsteroid() { | |
| asteroids.push(new Asteroid()); | |
| } | |
| // Update game | |
| function update() { | |
| if (!gameRunning) return; | |
| // Update player position (smooth follow) | |
| const dx = mouseX - player.x; | |
| const dy = mouseY - player.y; | |
| player.x += dx * 0.1; | |
| player.y += dy * 0.1; | |
| // Keep player on screen | |
| player.x = Math.max(player.width / 2, Math.min(canvas.width - player.width / 2, player.x)); | |
| player.y = Math.max(player.height / 2, Math.min(canvas.height - player.height / 2, player.y)); | |
| // Auto-fire bullets | |
| if (Math.random() < 0.15) { | |
| bullets.push(new Bullet(player.x, player.y - player.height / 2)); | |
| } | |
| // Update asteroids | |
| asteroids.forEach(asteroid => asteroid.update()); | |
| // Update bullets | |
| bullets.forEach(bullet => bullet.update()); | |
| // Update particles | |
| particles.forEach(particle => particle.update()); | |
| // Check collisions | |
| asteroids.forEach((asteroid, aIndex) => { | |
| // Check collision with player | |
| if (asteroid.collidesWith({ | |
| x: player.x - player.width / 2, | |
| y: player.y - player.height / 2, | |
| width: player.width, | |
| height: player.height | |
| })) { | |
| asteroids.splice(aIndex, 1); | |
| lives--; | |
| createExplosion(player.x, player.y, '#ff6b6b'); | |
| updateUI(); | |
| if (lives <= 0) { | |
| gameOver(); | |
| } | |
| } | |
| // Check collision with bullets | |
| bullets.forEach((bullet, bIndex) => { | |
| if (asteroid.collidesWith(bullet)) { | |
| asteroids.splice(aIndex, 1); | |
| bullets.splice(bIndex, 1); | |
| score += Math.floor(asteroid.width); | |
| createExplosion(asteroid.x + asteroid.width / 2, asteroid.y + asteroid.height / 2, '#ff6b6b'); | |
| updateUI(); | |
| } | |
| }); | |
| }); | |
| // Remove off-screen objects | |
| asteroids = asteroids.filter(asteroid => !asteroid.isOffScreen()); | |
| bullets = bullets.filter(bullet => !bullet.isOffScreen()); | |
| particles = particles.filter(particle => !particle.isDead()); | |
| // Spawn new asteroids | |
| asteroidSpawnTimer++; | |
| const spawnRate = Math.max(30, 120 - Math.floor(score / 100) * 10); | |
| if (asteroidSpawnTimer > spawnRate) { | |
| spawnAsteroid(); | |
| asteroidSpawnTimer = 0; | |
| } | |
| } | |
| // Draw game | |
| function draw() { | |
| // Clear canvas | |
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| // Draw player spaceship | |
| ctx.save(); | |
| ctx.translate(player.x, player.y); | |
| // Draw ship body | |
| ctx.fillStyle = '#4ecdc4'; | |
| ctx.beginPath(); | |
| ctx.moveTo(0, -player.height / 2); | |
| ctx.lineTo(-player.width / 2, player.height / 2); | |
| ctx.lineTo(0, player.height / 3); | |
| ctx.lineTo(player.width / 2, player.height / 2); | |
| ctx.closePath(); | |
| ctx.fill(); | |
| // Draw ship details | |
| ctx.fillStyle = '#ffffff'; | |
| ctx.beginPath(); | |
| ctx.arc(0, 0, 5, 0, Math.PI * 2); | |
| ctx.fill(); | |
| ctx.restore(); | |
| // Draw asteroids | |
| asteroids.forEach(asteroid => asteroid.draw()); | |
| // Draw bullets | |
| bullets.forEach(bullet => bullet.draw()); | |
| // Draw particles | |
| particles.forEach(particle => particle.draw()); | |
| } | |
| // Game loop | |
| function gameLoop() { | |
| update(); | |
| draw(); | |
| requestAnimationFrame(gameLoop); | |
| } | |
| // Update UI | |
| function updateUI() { | |
| scoreElement.textContent = score; | |
| livesElement.textContent = lives; | |
| } | |
| // Start game | |
| function startGame() { | |
| gameRunning = true; | |
| score = 0; | |
| lives = 3; | |
| asteroids = []; | |
| bullets = []; | |
| particles = []; | |
| asteroidSpawnTimer = 0; | |
| mouseX = canvas.width / 2; | |
| mouseY = canvas.height - 100; | |
| updateUI(); | |
| startScreen.classList.add('hidden'); | |
| gameOverScreen.classList.add('hidden'); | |
| } | |
| // Game over | |
| function gameOver() { | |
| gameRunning = false; | |
| finalScoreElement.textContent = `Final Score: ${score}`; | |
| let message = ''; | |
| if (score < 100) { | |
| message = "Keep practicing! You'll get better!"; | |
| } else if (score < 500) { | |
| message = "Great job! You're getting the hang of it!"; | |
| } else if (score < 1000) { | |
| message = "Excellent! You're a space defender!"; | |
| } else { | |
| message = "Incredible! You're a legendary pilot!"; | |
| } | |
| gameOverMessageElement.textContent = message; | |
| gameOverScreen.classList.remove('hidden'); | |
| } | |
| // Event listeners | |
| startButton.addEventListener('click', startGame); | |
| restartButton.addEventListener('click', startGame); | |
| // Start game loop | |
| gameLoop(); | |
| // Handle window resize | |
| window.addEventListener('resize', () => { | |
| resizeCanvas(); | |
| player.x = canvas.width / 2; | |
| player.y = canvas.height - 100; | |
| }); | |
| // Ensure video plays (some browsers require interaction) | |
| document.addEventListener('click', () => { | |
| if (video.paused) { | |
| video.play().catch(e => console.log('Video play failed:', e)); | |
| } | |
| }, { once: true }); | |
| </script> | |
| </body> | |
| </html> |