flappy-bird-V1 / index.html
royam0820's picture
Add 2 files
cc912fd verified
<!DOCTYPE html>
<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>