![]() |
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flappy Bird Game - Crow Edition</title>
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<style>
body {
margin: 0;
padding: 0;
font-family: 'Arial', sans-serif;
background-color: #333;
overflow: hidden;
}
.game-container {
position: relative;
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
canvas {
border: 1px solid #000;
max-width: 100%;
max-height: 100%;
}
.game-ui {
position: absolute;
top: 0;
left: 0;
width: 100%;
padding: 20px;
z-index: 10;
pointer-events: none;
}
.score {
font-size: 40px;
font-weight: bold;
color: white;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
text-align: center;
}
.start-screen, .game-over {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: rgba(0, 0, 0, 0.5);
z-index: 20;
}
.btn {
padding: 10px 20px;
font-size: 18px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
margin-top: 20px;
pointer-events: auto;
}
.btn:hover {
background-color: #45a049;
}
.title {
font-size: 48px;
font-weight: bold;
color: white;
margin-bottom: 20px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
}
.game-over-score {
font-size: 32px;
color: white;
margin-bottom: 20px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
}
</style>
</head>
<body class="bg-gray-800">
<div class="game-container">
<canvas id="gameCanvas" width="320" height="480"></canvas>
<div class="game-ui">
<div class="score" id="score">0</div>
</div>
<div class="start-screen" id="startScreen">
<div class="title">Flappy Crow</div>
<p class="text-white text-xl mb-4">Click or press Spacebar to flap</p>
<button class="btn" id="startButton">Start Game</button>
</div>
<div class="game-over" id="gameOverScreen" style="display: none;">
<div class="title">Game Over</div>
<div class="game-over-score" id="finalScore">Score: 0</div>
<button class="btn" id="restartButton">Play Again</button>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreDisplay = document.getElementById('score');
const finalScoreDisplay = document.getElementById('finalScore');
const startScreen = document.getElementById('startScreen');
const gameOverScreen = document.getElementById('gameOverScreen');
const startButton = document.getElementById('startButton');
const restartButton = document.getElementById('restartButton');
// Game variables
let gameRunning = false;
let gameOver = false;
let score = 0;
let frames = 0;
let daytime = true;
let cycleCounter = 0;
// Bird settings
const bird = {
x: 50,
y: canvas.height / 2,
width: 38,
height: 28,
gravity: 0.25,
velocity: 0,
jump: 4.6,
rotation: 0,
draw: function() {
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(this.rotation);
// Draw crow body (black)
ctx.fillStyle = '#000000'; // Crow body - pure black
ctx.beginPath();
ctx.arc(0, 0, 13, 0, Math.PI * 2);
ctx.fill();
// Crow tail feathers
ctx.beginPath();
ctx.moveTo(-8, 0);
ctx.lineTo(-20, -5);
ctx.lineTo(-20, 5);
ctx.closePath();
ctx.fill();
// Wing - slightly different shade
ctx.fillStyle = '#191919';
ctx.beginPath();
ctx.ellipse(-2, 5, 12, 7, 0, 0, Math.PI * 2);
ctx.fill();
// Wing detail
ctx.strokeStyle = '#333333';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(-10, 3);
ctx.lineTo(5, 7);
ctx.stroke();
// Eye
ctx.fillStyle = 'white';
ctx.beginPath();
ctx.arc(8, -4, 4, 0, Math.PI * 2);
ctx.fill();
// Pupil
ctx.fillStyle = 'black';
ctx.beginPath();
ctx.arc(9, -4, 2, 0, Math.PI * 2);
ctx.fill();
// Beak
ctx.fillStyle = '#333333'; // Dark gray beak
ctx.beginPath();
ctx.moveTo(10, 0);
ctx.lineTo(22, -1);
ctx.lineTo(22, 1);
ctx.closePath();
ctx.fill();
// Head feather tufts (crow's slight head crest)
ctx.fillStyle = '#000000';
ctx.beginPath();
ctx.moveTo(0, -10);
ctx.lineTo(5, -16);
ctx.lineTo(8, -12);
ctx.closePath();
ctx.fill();
ctx.restore();
},
flap: function() {
this.velocity = -this.jump;
},
update: function() {
// Apply gravity
this.velocity += this.gravity;
this.y += this.velocity;
// Limit bird movement to canvas bounds
if (this.y + this.height/2 >= canvas.height - foreground.height) {
this.y = canvas.height - foreground.height - this.height/2;
this.velocity = 0;
gameRunning = false;
gameOver = true;
}
if (this.y - this.height/2 <= 0) {
this.y = this.height/2;
this.velocity = 0;
}
// Set bird rotation based on velocity
if (this.velocity >= 5) {
this.rotation = Math.PI/4; // 45 degrees down
} else if (this.velocity <= 0) {
this.rotation = -Math.PI/6; // 30 degrees up
} else {
this.rotation = 0;
}
}
};
// Background
const background = {
x: 0,
y: 0,
width: canvas.width,
height: canvas.height - 112,
draw: function() {
// Create gradient background instead of solid color
let skyGradient;
if (daytime) {
// Day sky - blue gradient
skyGradient = ctx.createLinearGradient(0, 0, 0, this.height);
skyGradient.addColorStop(0, '#1E90FF'); // Deep blue at top
skyGradient.addColorStop(0.7, '#87CEEB'); // Sky blue
skyGradient.addColorStop(1, '#E0F7FA'); // Light blue near horizon
} else {
// Night sky - dark blue gradient with stars
skyGradient = ctx.createLinearGradient(0, 0, 0, this.height);
skyGradient.addColorStop(0, '#000033'); // Deep blue at top
skyGradient.addColorStop(0.7, '#0A1172'); // Midnight blue
skyGradient.addColorStop(1, '#1F4788'); // Lighter blue near horizon
}
// Fill background with gradient
ctx.fillStyle = skyGradient;
ctx.fillRect(0, 0, canvas.width, this.height);
// Add sun or moon
if (daytime) {
// Sun
ctx.fillStyle = '#FFFF00';
ctx.beginPath();
ctx.arc(canvas.width - 50, 50, 30, 0, Math.PI * 2);
ctx.fill();
// Sun rays
ctx.strokeStyle = '#FFFF00';
ctx.lineWidth = 2;
for (let i = 0; i < 8; i++) {
ctx.beginPath();
ctx.moveTo(canvas.width - 50, 50);
let angle = i * Math.PI / 4;
ctx.lineTo(
canvas.width - 50 + Math.cos(angle) * 45,
50 + Math.sin(angle) * 45
);
ctx.stroke();
}
// Fluffy clouds
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
// Cloud 1
ctx.beginPath();
ctx.arc(80, 80, 25, 0, Math.PI * 2);
ctx.arc(120, 70, 35, 0, Math.PI * 2);
ctx.arc(160, 85, 25, 0, Math.PI * 2);
ctx.fill();
// Cloud 2
ctx.beginPath();
ctx.arc(250, 100, 30, 0, Math.PI * 2);
ctx.arc(290, 90, 40, 0, Math.PI * 2);
ctx.arc(330, 105, 30, 0, Math.PI * 2);
ctx.fill();
} else {
// Moon
ctx.fillStyle = '#FFFFCC';
ctx.beginPath();
ctx.arc(canvas.width - 50, 50, 25, 0, Math.PI * 2);
ctx.fill();
// Moon crater 1
ctx.fillStyle = 'rgba(200, 200, 200, 0.3)';
ctx.beginPath();
ctx.arc(canvas.width - 60, 40, 7, 0, Math.PI * 2);
ctx.fill();
// Moon crater 2
ctx.beginPath();
ctx.arc(canvas.width - 45, 60, 5, 0, Math.PI * 2);
ctx.fill();
// Stars
ctx.fillStyle = 'white';
const stars = [
{x: 30, y: 50}, {x: 70, y: 30}, {x: 150, y: 60},
{x: 200, y: 40}, {x: 250, y: 70}, {x: 100, y: 90},
{x: 180, y: 120}, {x: 220, y: 150}, {x: 120, y: 170}
];
stars.forEach(star => {
ctx.beginPath();
ctx.arc(star.x, star.y, 1.5, 0, Math.PI * 2);
ctx.fill();
});
// Draw some twinkling stars
const twinkleFactor = Math.sin(frames * 0.05) * 0.5 + 0.5;
ctx.fillStyle = `rgba(255, 255, 255, ${twinkleFactor})`;
const twinklingStars = [
{x: 50, y: 70}, {x: 120, y: 40}, {x: 170, y: 80},
{x: 240, y: 60}, {x: 280, y: 90}, {x: 80, y: 110}
];
twinklingStars.forEach(star => {
ctx.beginPath();
ctx.arc(star.x, star.y, 2, 0, Math.PI * 2);
ctx.fill();
});
}
// Add distant mountains
const mountainColor = daytime ? '#7986CB' : '#303F9F';
ctx.fillStyle = mountainColor;
// Mountain range
ctx.beginPath();
ctx.moveTo(0, this.height);
ctx.lineTo(0, this.height - 80);
ctx.lineTo(40, this.height - 100);
ctx.lineTo(80, this.height - 60);
ctx.lineTo(120, this.height - 130);
ctx.lineTo(180, this.height - 80);
ctx.lineTo(220, this.height - 110);
ctx.lineTo(280, this.height - 70);
ctx.lineTo(320, this.height - 90);
ctx.lineTo(320, this.height);
ctx.closePath();
ctx.fill();
// Snow caps on mountains if daytime
if (daytime) {
ctx.fillStyle = 'white';
ctx.beginPath();
ctx.moveTo(115, this.height - 130);
ctx.lineTo(120, this.height - 130);
ctx.lineTo(125, this.height - 125);
ctx.lineTo(115, this.height - 125);
ctx.closePath();
ctx.fill();
ctx.beginPath();
ctx.moveTo(215, this.height - 110);
ctx.lineTo(220, this.height - 110);
ctx.lineTo(225, this.height - 105);
ctx.lineTo(215, this.height - 105);
ctx.closePath();
ctx.fill();
}
}
};
// Foreground
const foreground = {
x: 0,
y: canvas.height - 112,
width: canvas.width,
height: 112,
dx: 2, // Scroll speed
draw: function() {
// Ground gradient
const groundGradient = ctx.createLinearGradient(0, this.y, 0, canvas.height);
if (daytime) {
groundGradient.addColorStop(0, '#7CFC00'); // Grass top
groundGradient.addColorStop(0.2, '#5EAE5E'); // Grass middle
groundGradient.addColorStop(1, '#8B4513'); // Dirt bottom
} else {
groundGradient.addColorStop(0, '#3A5F0B'); // Dark grass top
groundGradient.addColorStop(0.2, '#2F4F2F'); // Dark grass middle
groundGradient.addColorStop(1, '#3D2314'); // Dark dirt bottom
}
ctx.fillStyle = groundGradient;
ctx.fillRect(0, this.y, canvas.width, this.height);
// Draw grass tufts
ctx.fillStyle = daytime ? '#7CFC00' : '#3A5F0B';
for (let i = 0; i < canvas.width; i += 15) {
const height = 5 + Math.random() * 5;
ctx.fillRect(i, this.y, 3, -height);
}
// Draw a path
ctx.fillStyle = daytime ? '#D2B48C' : '#5D4037';
ctx.fillRect(0, this.y + 20, canvas.width, 10);
// Draw pebbles on the path
ctx.fillStyle = daytime ? '#A9A9A9' : '#424242';
for (let i = 0; i < canvas.width; i += 20) {
const x = i + Math.random() * 10;
ctx.beginPath();
ctx.arc(x, this.y + 25, 2, 0, Math.PI * 2);
ctx.fill();
}
},
update: function() {
if (gameRunning) {
this.x = (this.x - this.dx) % (canvas.width/4);
}
}
};
// Pipes
const pipes = {
position: [],
gap: 100,
maxYPos: -150,
dx: 2,
draw: function() {
for (let i = 0; i < this.position.length; i++) {
let p = this.position[i];
// Pipe gradient
const pipeGradient = ctx.createLinearGradient(p.x, 0, p.x + p.width, 0);
if (daytime) {
pipeGradient.addColorStop(0, '#32CD32'); // Left edge
pipeGradient.addColorStop(0.5, '#73BF2E'); // Middle
pipeGradient.addColorStop(1, '#32CD32'); // Right edge
} else {
pipeGradient.addColorStop(0, '#1B5E20'); // Left edge
pipeGradient.addColorStop(0.5, '#2E7D32'); // Middle
pipeGradient.addColorStop(1, '#1B5E20'); // Right edge
}
// Top pipe
ctx.fillStyle = pipeGradient;
ctx.fillRect(p.x, p.y, p.width, p.height);
// Pipe cap gradient
const capGradient = ctx.createLinearGradient(p.x - 2, 0, p.x + p.width + 4, 0);
if (daytime) {
capGradient.addColorStop(0, '#228B22'); // Left edge
capGradient.addColorStop(0.5, '#588A1B'); // Middle
capGradient.addColorStop(1, '#228B22'); // Right edge
} else {
capGradient.addColorStop(0, '#0F3D0F'); // Left edge
capGradient.addColorStop(0.5, '#1B4D1B'); // Middle
capGradient.addColorStop(1, '#0F3D0F'); // Right edge
}
// Pipe cap
ctx.fillStyle = capGradient;
ctx.fillRect(p.x - 2, p.y + p.height - 10, p.width + 4, 10);
// Bottom pipe
let bottomPipeY = p.y + p.height + this.gap;
ctx.fillStyle = pipeGradient;
ctx.fillRect(p.x, bottomPipeY, p.width, canvas.height - bottomPipeY - foreground.height);
// Pipe cap
ctx.fillStyle = capGradient;
ctx.fillRect(p.x - 2, bottomPipeY, p.width + 4, 10);
}
},
update: function() {
if (!gameRunning) return;
if (frames % 100 === 0) {
this.position.push({
x: canvas.width,
y: this.maxYPos * (Math.random() + 1),
width: 52,
height: 320
});
}
for (let i = 0; i < this.position.length; i++) {
let p = this.position[i];
// Move pipes to the left
p.x -= this.dx;
// If a pipe goes off screen, remove it
if (p.x + p.width < 0) {
this.position.shift();
// Increment score when pipe passes
score++;
scoreDisplay.innerText = score;
}
// Check for collision
// Top pipe
if (
bird.x + bird.width/2 > p.x &&
bird.x - bird.width/2 < p.x + p.width &&
bird.y - bird.height/2 < p.y + p.height &&
bird.y + bird.height/2 > p.y
) {
gameRunning = false;
gameOver = true;
}
// Bottom pipe
if (
bird.x + bird.width/2 > p.x &&
bird.x - bird.width/2 < p.x + p.width &&
bird.y - bird.height/2 < p.y + p.height + this.gap + (canvas.height - p.y - p.height - this.gap - foreground.height) &&
bird.y + bird.height/2 > p.y + p.height + this.gap
) {
gameRunning = false;
gameOver = true;
}
}
}
};
// Day/Night cycle
function updateDayNightCycle() {
if (gameRunning) {
cycleCounter++;
// Change between day and night every 30 seconds (approximately 1800 frames)
if (cycleCounter >= 1800) {
daytime = !daytime;
cycleCounter = 0;
}
}
}
// Game functions
function draw() {
background.draw();
pipes.draw();
foreground.draw();
bird.draw();
}
function update() {
bird.update();
foreground.update();
pipes.update();
updateDayNightCycle();
}
function loop() {
update();
draw();
frames++;
if (gameOver) {
finalScoreDisplay.innerText = `Score: ${score}`;
gameOverScreen.style.display = 'flex';
return;
}
requestAnimationFrame(loop);
}
function init() {
gameRunning = true;
gameOver = false;
score = 0;
frames = 0;
cycleCounter = 0;
daytime = true;
bird.y = canvas.height / 2;
bird.velocity = 0;
pipes.position = [];
scoreDisplay.innerText = score;
startScreen.style.display = 'none';
gameOverScreen.style.display = 'none';
loop();
}
// Event Listeners
startButton.addEventListener('click', init);
restartButton.addEventListener('click', init);
canvas.addEventListener('click', () => {
if (gameRunning) {
bird.flap();
}
});
document.addEventListener('keydown', (e) => {
if (e.code === 'Space' && gameRunning) {
bird.flap();
}
});
// Initial draw
draw();
});
</script>
</body>
</html>
0 Comments