Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Flappy Bird - Easy Control Mode</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap'); | |
| .flappy-bird { | |
| font-family: 'Press Start 2P', cursive; | |
| } | |
| #gameCanvas { | |
| display: block; | |
| background-color: #70c5ce; | |
| width: 600px; | |
| } | |
| .bird { | |
| background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48cGF0aCBmaWxsPSIjZjlkYzAwIiBkPSJNNDQ4IDI1NmMwLTEwNi4wMzktODUuOTYxLTE5Mi0xOTItMTkyUzY0IDE0OS45NjEgNjQgMjU2YzAgMTA2LjAzOSA4NS45NjEgMTkyIDE5MiAxOTJzMTkyLTg1Ljk2MSAxOTItMTkyeiIvPjxwYXRoIGZpbGw9IiNmZmZlYjkiIGQ9Ik0yODggMTkyYzAtMTcuNjczLTE0LjMyNy0zMi0zMi0zMnMtMzIgMTQuMzI3LTMyIDMyIDE0LjMyNyAzMiAzMiAzMlMzODggMjA5LjY3MyAzODggMTkyeiBNMzc2IDE2MGMwLTE3LjY3My0xNC4zMjctMzItMzItMzJzLTMyIDE0LjMyNy0zMiAzMiAxNC4zMjcgMzIgMzIgMzJTMzc2IDE3Ny42NzMgMzc2IDE2MHogTTM0NCAxOTJjMC0xNy42NzMtMTQuMzI3LTMyLTMyLTMycy0zMiAxNC4zMjctMzIgMzIgMTQuMzI3IDMyIDMyIDMyUzM0NCAyMDkuNjczIDM0NCAxOTJ6Ii8+PHBhdGggZmlsbD0iI2Y1YTYyNyIgZD0iTTI4OCAxOTJjMC0xNy42NzMtMTQuMzI3LTMyLTMyLTMycy0zMiAxNC4zMjctMzIgMzIgMTQuMzI3IDMyIDMyIDMyUzI4OCAyMDkuNjczIDI4OCAxOTJ6Ii8+PHBhdGggZmlsbD0iI2U1MzkzNSIgZD0iTTQwOCAzMjBjMC0yNi40NjctMjEuNTMzLTQ4LTQ4LTQ4cy00OCAyMS41MzMtNDggNDggMjEuNTMzIDQ4IDQ4IDQ4IDQ4LTIxLjUzMyA0OC00OHoiLz48cGF0aCBmaWxsPSIjZTUzOTM1IiBkPSJNMzUyIDI4OGMwLTE3LjY3My0xNC4zMjctMzItMzItMzJzLTMyIDE0LjMyNy0zMiAzMiAxNC4zMjcgMzIgMzIgMzJTMzUyIDMwNS42NzMgMzUyIDI4OHoiLz48cGF0aCBmaWxsPSIjZTUzOTM1IiBkPSJNMjg4IDI0MGMwLTE3LjY3My0xNC4zMjctMzItMzItMzJzLTMyIDE0LjMyNy0zMiAzMiAxNC4zMjcgMzIgMzIgMzJTMjg4IDI1Ny42NzMgMjg4IDI0MHoiLz48cGF0aCBmaWxsPSIjZTUzOTM1IiBkPSJNMjAzLjI1IDExOC4xMjVjMC0yNi40NjctMjEuNTMzLTQ4LTQ4LTQ4cy00OCAyMS41MzMtNDggNDggMjEuNTMzIDQ4IDQ4IDQ4IDQ4LTIxLjUzMyA0OC00OHoiLz48L3N2Zw=='); | |
| background-size: cover; | |
| position: absolute; | |
| } | |
| .pipe { | |
| position: absolute; | |
| background-color: #5cb85c; | |
| border: 3px solid #4cae4c; | |
| border-radius: 5px; | |
| } | |
| .pipe-top { | |
| border-bottom-left-radius: 0; | |
| border-bottom-right-radius: 0; | |
| } | |
| .pipe-bottom { | |
| border-top-left-radius: 0; | |
| border-top-right-radius: 0; | |
| } | |
| .score-display { | |
| text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5); | |
| color: white; | |
| pointer-events: none; | |
| } | |
| .game-over { | |
| background-color: rgba(0, 0, 0, 0.7); | |
| border: 3px solid white; | |
| } | |
| .instructions { | |
| animation: pulse 1.5s infinite; | |
| } | |
| .difficulty-indicator { | |
| position: absolute; | |
| bottom: 10px; | |
| left: 0; | |
| right: 0; | |
| text-align: center; | |
| color: white; | |
| text-shadow: 1px 1px 2px black; | |
| font-size: 12px; | |
| } | |
| .jump-animation { | |
| animation: jumpScale 0.2s ease-out; | |
| } | |
| @keyframes pulse { | |
| 0% { transform: scale(1); opacity: 1; } | |
| 50% { transform: scale(1.05); opacity: 0.8; } | |
| 100% { transform: scale(1); opacity: 1; } | |
| } | |
| @keyframes jumpScale { | |
| 0% { transform: scale(1); } | |
| 50% { transform: scale(0.9); } | |
| 100% { transform: scale(1); } | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-900 flex items-center justify-center min-h-screen flappy-bird"> | |
| <div class="relative overflow-hidden border-4 border-white rounded-lg shadow-2xl"> | |
| <canvas id="gameCanvas" width="600" height="600"></canvas> | |
| <div id="startScreen" class="absolute inset-0 flex flex-col items-center justify-center bg-gray-900 bg-opacity-70 text-white p-4 text-center"> | |
| <h1 class="text-4xl mb-8 text-yellow-300">FLAPPY BIRD</h1> | |
| <p class="text-xl mb-2">Gentle Controls Mode</p> | |
| <p class="text-lg instructions">Press SPACE or CLICK to start</p> | |
| <div class="mt-8 text-sm"> | |
| <p>Controls:</p> | |
| <p>SPACE, CLICK, or ARROW UP - Gentle Jump</p> | |
| <p class="mt-4 text-xs">Game becomes slightly more challenging as you play</p> | |
| </div> | |
| </div> | |
| <div id="gameOver" class="hidden absolute inset-0 flex flex-col items-center justify-center bg-black bg-opacity-70 text-white p-4 text-center game-over"> | |
| <h2 class="text-3xl mb-4 text-red-500">GAME OVER</h2> | |
| <p id="finalScore" class="text-2xl mb-6">Score: 0</p> | |
| <button id="restartBtn" class="px-6 py-2 bg-green-500 hover:bg-green-600 rounded-lg text-lg transition-colors duration-200 jump-animation"> | |
| PLAY AGAIN | |
| </button> | |
| </div> | |
| <div id="scoreDisplay" class="hidden absolute top-4 left-0 right-0 text-center text-2xl font-bold score-display">0</div> | |
| <div id="difficultyDisplay" class="difficulty-indicator hidden">Difficulty: Gentle</div> | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Game elements | |
| const canvas = document.getElementById('gameCanvas'); | |
| const ctx = canvas.getContext('2d'); | |
| const startScreen = document.getElementById('startScreen'); | |
| const gameOverScreen = document.getElementById('gameOver'); | |
| const scoreDisplay = document.getElementById('scoreDisplay'); | |
| const finalScoreDisplay = document.getElementById('finalScore'); | |
| const restartBtn = document.getElementById('restartBtn'); | |
| const difficultyDisplay = document.getElementById('difficultyDisplay'); | |
| // Game variables | |
| let gameRunning = false; | |
| let score = 0; | |
| let highScore = localStorage.getItem('flappyHighScore') || 0; | |
| let difficultyLevel = 0; | |
| const MAX_DIFFICULTY = 5; | |
| const DIFFICULTY_NAMES = ["Gentle", "Easy", "Medium", "Challenging", "Hard", "Expert"]; | |
| // Bird properties (softer controls) | |
| const bird = { | |
| x: 150, // Starting more to the right with wider screen | |
| y: canvas.height / 2, | |
| width: 40, | |
| height: 30, | |
| velocity: 0, | |
| gravity: 0.2, // Very light gravity | |
| jumpForce: -5, // Mild jump force for better control | |
| color: '#FFD700', | |
| rotation: 0 | |
| }; | |
| // Pipes (with very easy starting settings) | |
| let pipes = []; | |
| const pipeWidth = 70; | |
| const pipeGap = 180; // Very wide gap at start | |
| let pipeGapCurrent = pipeGap; | |
| const pipeFrequency = 2000; // milliseconds (slow at first) | |
| let pipeSpeed = 1.8; // Slow initial speed | |
| let lastPipeTime = 0; | |
| // Game loop variables | |
| let animationId; | |
| let lastTimestamp; | |
| let lastJumpTime = 0; | |
| const jumpCooldown = 150; // milliseconds between jumps | |
| // Start game function | |
| function startGame() { | |
| // Reset game state | |
| gameRunning = true; | |
| score = 0; | |
| difficultyLevel = 0; | |
| bird.y = canvas.height / 2; | |
| bird.velocity = 0; | |
| bird.rotation = 0; | |
| pipes = []; | |
| pipeGapCurrent = pipeGap; | |
| pipeSpeed = 1.8; | |
| // Hide screens | |
| startScreen.classList.add('hidden'); | |
| gameOverScreen.classList.add('hidden'); | |
| scoreDisplay.textContent = '0'; | |
| scoreDisplay.classList.remove('hidden'); | |
| difficultyDisplay.textContent = 'Difficulty: ' + DIFFICULTY_NAMES[difficultyLevel]; | |
| difficultyDisplay.classList.remove('hidden'); | |
| // Start game loop | |
| lastTimestamp = performance.now(); | |
| animationId = requestAnimationFrame(gameLoop); | |
| } | |
| // Game over function | |
| function gameOver() { | |
| gameRunning = false; | |
| cancelAnimationFrame(animationId); | |
| // Update high score | |
| if (score > highScore) { | |
| highScore = score; | |
| localStorage.setItem('flappyHighScore', highScore); | |
| } | |
| // Show game over screen | |
| finalScoreDisplay.textContent = `Score: ${score} | High Score: ${highScore}`; | |
| gameOverScreen.classList.remove('hidden'); | |
| scoreDisplay.classList.add('hidden'); | |
| difficultyDisplay.classList.add('hidden'); | |
| // Add animation to restart button | |
| restartBtn.classList.add('jump-animation'); | |
| setTimeout(() => { | |
| restartBtn.classList.remove('jump-animation'); | |
| }, 200); | |
| } | |
| // Update difficulty based on score - VERY gradual | |
| function updateDifficulty() { | |
| const newDifficulty = Math.min( | |
| Math.floor(score / 5), // Only increase every 5 points | |
| MAX_DIFFICULTY | |
| ); | |
| if (newDifficulty !== difficultyLevel) { | |
| difficultyLevel = newDifficulty; | |
| // Adjust game parameters based on difficulty - very small increments | |
| pipeSpeed = 1.8 + (difficultyLevel * 0.3); // 1.8 to 3.3 | |
| pipeGapCurrent = Math.max(120, pipeGap - (difficultyLevel * 12)); // 180 to 120 | |
| bird.gravity = 0.2 + (difficultyLevel * 0.05); // 0.2 to 0.45 | |
| // Update UI | |
| difficultyDisplay.textContent = 'Difficulty: ' + DIFFICULTY_NAMES[difficultyLevel]; | |
| } | |
| } | |
| // Main game loop | |
| function gameLoop(timestamp) { | |
| // Calculate delta time | |
| const deltaTime = timestamp - lastTimestamp; | |
| lastTimestamp = timestamp; | |
| // Clear canvas | |
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| // Update and draw background | |
| drawBackground(); | |
| // Update and draw bird | |
| updateBird(deltaTime); | |
| drawBird(); | |
| // Update and draw pipes | |
| updatePipes(timestamp); | |
| drawPipes(); | |
| // Check collisions | |
| if (checkCollisions()) { | |
| gameOver(); | |
| return; | |
| } | |
| // Update difficulty (very gradually) | |
| updateDifficulty(); | |
| // Continue game loop if still running | |
| if (gameRunning) { | |
| animationId = requestAnimationFrame(gameLoop); | |
| } | |
| } | |
| // Draw background | |
| function drawBackground() { | |
| // Sky | |
| ctx.fillStyle = '#70c5ce'; | |
| ctx.fillRect(0, 0, canvas.width, canvas.height); | |
| // Ground (wider) | |
| ctx.fillStyle = '#5cb85c'; | |
| ctx.fillRect(0, canvas.height - 50, canvas.width, 50); | |
| // Ground texture | |
| ctx.strokeStyle = '#4cae4c'; | |
| ctx.lineWidth = 3; | |
| for (let i = 0; i < canvas.width; i += 20) { | |
| ctx.beginPath(); | |
| ctx.moveTo(i, canvas.height - 50); | |
| ctx.lineTo(i + 10, canvas.height - 40); | |
| ctx.stroke(); | |
| } | |
| } | |
| // Update bird physics (very gentle) | |
| function updateBird(deltaTime) { | |
| // Apply gravity | |
| bird.velocity += bird.gravity * (deltaTime / 16); // Normalize for frame rate | |
| // Update position | |
| bird.y += bird.velocity; | |
| // Calculate rotation based on velocity (smoother) | |
| bird.rotation = Math.min(Math.max(bird.velocity * 3, -Math.PI/4), Math.PI/4); | |
| // Keep bird in bounds | |
| if (bird.y < 0) { | |
| bird.y = 0; | |
| bird.velocity = 0; | |
| } | |
| // Check if bird hit the ground | |
| if (bird.y + bird.height > canvas.height - 50) { | |
| bird.y = canvas.height - 50 - bird.height; | |
| bird.velocity = 0; | |
| } | |
| } | |
| // Draw bird with smooth rotation | |
| function drawBird() { | |
| ctx.save(); | |
| ctx.translate(bird.x + bird.width/2, bird.y + bird.height/2); | |
| ctx.rotate(bird.rotation); | |
| // Draw bird body (centered on transform point) | |
| ctx.fillStyle = bird.color; | |
| ctx.beginPath(); | |
| ctx.ellipse(0, 0, bird.width/2, bird.height/2, 0, 0, Math.PI * 2); | |
| ctx.fill(); | |
| // Draw eye | |
| ctx.fillStyle = 'black'; | |
| ctx.beginPath(); | |
| ctx.arc(bird.width * 0.2, -bird.height * 0.2, 3, 0, Math.PI * 2); | |
| ctx.fill(); | |
| // Draw beak | |
| ctx.fillStyle = '#FFA500'; | |
| ctx.beginPath(); | |
| ctx.moveTo(bird.width/2, 0); | |
| ctx.lineTo(bird.width/2 + 15, 5); | |
| ctx.lineTo(bird.width/2 + 15, -5); | |
| ctx.closePath(); | |
| ctx.fill(); | |
| // Draw wing (flapping effect) | |
| ctx.fillStyle = '#FF8C00'; | |
| const wingY = Math.sin(Date.now() / 100) * 5; | |
| ctx.beginPath(); | |
| ctx.moveTo(-bird.width * 0.1, wingY); | |
| ctx.lineTo(bird.width * 0.1, bird.height * 0.2 + wingY); | |
| ctx.lineTo(-bird.width * 0.2, bird.height * 0.3 + wingY); | |
| ctx.closePath(); | |
| ctx.fill(); | |
| ctx.restore(); | |
| } | |
| // Make bird jump (with cooldown to prevent spamming) | |
| function jump() { | |
| const now = Date.now(); | |
| if (now - lastJumpTime < jumpCooldown) return; | |
| lastJumpTime = now; | |
| if (!gameRunning && !gameOverScreen.classList.contains('hidden')) return; | |
| if (!gameRunning) { | |
| startGame(); | |
| } else { | |
| bird.velocity = bird.jumpForce; | |
| // Add visual feedback | |
| const scoreEl = scoreDisplay; | |
| scoreEl.classList.add('jump-animation'); | |
| setTimeout(() => scoreEl.classList.remove('jump-animation'), 200); | |
| } | |
| } | |
| // Update pipes | |
| function updatePipes(timestamp) { | |
| // Add new pipes | |
| if (timestamp - lastPipeTime > pipeFrequency / (1 + difficultyLevel * 0.2)) { | |
| const minGapFromEdge = 80; | |
| const gapPosition = minGapFromEdge + | |
| Math.random() * (canvas.height - pipeGapCurrent - minGapFromEdge * 2 - 50); // Leave room at bottom | |
| pipes.push({ | |
| x: canvas.width, | |
| width: pipeWidth, | |
| topHeight: gapPosition, | |
| bottomHeight: canvas.height - gapPosition - pipeGapCurrent - 50, | |
| scored: false, | |
| colorTop: `hsl(${100 + difficultyLevel * 10}, 70%, 45%)`, | |
| colorBottom: `hsl(${100 + difficultyLevel * 10}, 70%, 40%)` | |
| }); | |
| lastPipeTime = timestamp; | |
| } | |
| // Move pipes and remove off-screen pipes | |
| pipes = pipes.filter(pipe => { | |
| pipe.x -= pipeSpeed; | |
| // Check if bird passed the pipe (for scoring) | |
| if (!pipe.scored && pipe.x + pipe.width < bird.x) { | |
| pipe.scored = true; | |
| score++; | |
| scoreDisplay.textContent = score; | |
| } | |
| return pipe.x + pipe.width > 0; | |
| }); | |
| } | |
| // Draw pipes | |
| function drawPipes() { | |
| pipes.forEach(pipe => { | |
| // Top pipe | |
| ctx.fillStyle = pipe.colorTop || '#5cb85c'; | |
| ctx.fillRect(pipe.x, 0, pipe.width, pipe.topHeight); | |
| // Pipe decoration (top) | |
| ctx.fillStyle = pipe.colorBottom || '#4cae4c'; | |
| ctx.fillRect(pipe.x - 3, pipe.topHeight - 20, pipe.width + 6, 20); | |
| // Bottom pipe | |
| ctx.fillStyle = pipe.colorTop || '#5cb85c'; | |
| ctx.fillRect(pipe.x, canvas.height - pipe.bottomHeight - 50, pipe.width, pipe.bottomHeight); | |
| // Pipe decoration (bottom) | |
| ctx.fillStyle = pipe.colorBottom || '#4cae4c'; | |
| ctx.fillRect(pipe.x - 3, canvas.height - pipe.bottomHeight - 50, pipe.width + 6, 20); | |
| }); | |
| } | |
| // Check collisions | |
| function checkCollisions() { | |
| // Check ground collision | |
| if (bird.y + bird.height >= canvas.height - 50) { | |
| return true; | |
| } | |
| // Check pipe collisions | |
| for (const pipe of pipes) { | |
| // Check if bird is within pipe's x-range | |
| if (bird.x + bird.width > pipe.x && bird.x < pipe.x + pipe.width) { | |
| // Check top pipe collision | |
| if (bird.y < pipe.topHeight) { | |
| return true; | |
| } | |
| // Check bottom pipe collision | |
| if (bird.y + bird.height > canvas.height - pipe.bottomHeight - 50) { | |
| return true; | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| // Event listeners (with debounce for space bar) | |
| document.addEventListener('keydown', function(e) { | |
| if (e.code === 'Space' || e.key === ' ' || e.key === 'ArrowUp') { | |
| e.preventDefault(); | |
| jump(); | |
| } | |
| }); | |
| canvas.addEventListener('click', function() { | |
| jump(); | |
| }); | |
| restartBtn.addEventListener('click', function() { | |
| this.classList.add('jump-animation'); | |
| setTimeout(() => this.classList.remove('jump-animation'), 200); | |
| setTimeout(startGame, 200); // Small delay for button animation | |
| }); | |
| // Display start screen | |
| startScreen.classList.remove('hidden'); | |
| // Add touch support for mobile with cooldown | |
| canvas.addEventListener('touchstart', function(e) { | |
| e.preventDefault(); | |
| jump(); | |
| }, { passive: false }); | |
| }); | |
| </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=royam0820/royam0820-space-games" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |