clean-tool-60 / index.html
MySafeCode's picture
Upload index.html
d365e05 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Minimal Flappy Bird</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: linear-gradient(to bottom, #64b3f4, #c2e59c);
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
overflow: hidden;
}
.game-container {
position: relative;
width: 360px;
text-align: center;
}
canvas {
background: #70c5ce;
border-radius: 10px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
display: block;
margin: 0 auto;
}
h1 {
color: #fff;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
margin-bottom: 15px;
font-size: 2.5rem;
}
.score-display {
position: absolute;
top: 20px;
left: 0;
right: 0;
text-align: center;
font-size: 50px;
font-weight: bold;
color: white;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
z-index: 10;
}
.instructions {
background: rgba(0, 0, 0, 0.7);
color: white;
padding: 15px;
border-radius: 10px;
margin-top: 20px;
font-size: 16px;
line-height: 1.5;
}
.game-over {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.85);
color: white;
padding: 30px;
border-radius: 15px;
text-align: center;
display: none;
z-index: 20;
width: 80%;
}
.game-over h2 {
font-size: 36px;
margin-bottom: 15px;
color: #ffcc00;
}
.final-score {
font-size: 28px;
margin: 15px 0;
}
button {
background: #ffcc00;
border: none;
padding: 12px 30px;
font-size: 18px;
border-radius: 50px;
cursor: pointer;
font-weight: bold;
margin-top: 15px;
transition: all 0.2s;
}
button:hover {
background: #ffd84d;
transform: scale(1.05);
}
.pipe-counter {
position: absolute;
bottom: 20px;
left: 0;
right: 0;
text-align: center;
color: white;
font-size: 18px;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}
</style>
</head>
<body>
<div class="game-container">
<h1>Flappy Bird</h1>
<div class="score-display">0</div>
<canvas id="gameCanvas" width="360" height="640"></canvas>
<div class="pipe-counter">Pipes Passed: <span id="pipesPassed">0</span></div>
<div class="instructions">
<p>Press SPACE, CLICK, or TOUCH to flap your wings!</p>
<p>Avoid the pipes and don't hit the ground!</p>
</div>
<div class="game-over" id="gameOverScreen">
<h2>Game Over!</h2>
<div class="final-score">Score: <span id="finalScore">0</span></div>
<div>Pipes Passed: <span id="finalPipes">0</span></div>
<button id="restartButton">Play Again</button>
</div>
</div>
<script>
// Game variables
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreDisplay = document.querySelector('.score-display');
const gameOverScreen = document.getElementById('gameOverScreen');
const finalScore = document.getElementById('finalScore');
const finalPipes = document.getElementById('finalPipes');
const restartButton = document.getElementById('restartButton');
const pipesPassedDisplay = document.getElementById('pipesPassed');
// Game state
let gameState = "start"; // start, playing, gameover
let score = 0;
let pipesPassed = 0;
let frames = 0;
// Bird properties
const bird = {
x: 50,
y: canvas.height / 2 - 10,
width: 34,
height: 24,
gravity: 0.5,
velocity: 0,
jump: -10,
draw: function() {
ctx.fillStyle = '#FFD700'; // Yellow body
ctx.beginPath();
ctx.arc(this.x, this.y, 15, 0, Math.PI * 2);
ctx.fill();
// Draw eye
ctx.fillStyle = 'black';
ctx.beginPath();
ctx.arc(this.x + 8, this.y - 3, 4, 0, Math.PI * 2);
ctx.fill();
// Draw beak
ctx.fillStyle = '#FF8C00';
ctx.beginPath();
ctx.moveTo(this.x + 15, this.y);
ctx.lineTo(this.x + 30, this.y);
ctx.lineTo(this.x + 15, this.y + 8);
ctx.fill();
// Draw wing
ctx.fillStyle = '#FFA500';
ctx.beginPath();
ctx.ellipse(this.x - 5, this.y + 5, 10, 7, 0, 0, Math.PI * 2);
ctx.fill();
},
update: function() {
if (gameState === "playing") {
this.velocity += this.gravity;
this.y += this.velocity;
// Floor collision
if (this.y + this.height/2 >= canvas.height - groundHeight) {
this.y = canvas.height - groundHeight - this.height/2;
if (gameState === "playing") {
gameOver();
}
}
// Ceiling collision
if (this.y - this.height/2 <= 0) {
this.y = this.height/2;
this.velocity = 0;
}
}
},
flap: function() {
this.velocity = this.jump;
},
reset: function() {
this.y = canvas.height / 2 - 10;
this.velocity = 0;
}
};
// Ground properties
const groundHeight = 80;
function drawGround() {
ctx.fillStyle = '#DEB887'; // Brown ground
ctx.fillRect(0, canvas.height - groundHeight, canvas.width, groundHeight);
// Draw grass
ctx.fillStyle = '#7CFC00'; // Green grass
ctx.fillRect(0, canvas.height - groundHeight, canvas.width, 15);
// Draw ground pattern
ctx.fillStyle = '#8B4513';
for (let i = 0; i < canvas.width; i += 30) {
ctx.fillRect(i, canvas.height - 20, 15, 5);
}
}
// Pipes
const pipes = {
position: [],
gap: 180,
maxYPos: -150,
dx: 2,
draw: function() {
for (let i = 0; i < this.position.length; i++) {
let p = this.position[i];
// Top pipe
ctx.fillStyle = '#228B22'; // Green pipe
ctx.fillRect(p.x, p.y, p.width, p.height);
// Pipe cap
ctx.fillStyle = '#006400';
ctx.fillRect(p.x - 5, p.y + p.height - 20, p.width + 10, 20);
// Bottom pipe
ctx.fillStyle = '#228B22';
ctx.fillRect(p.x, p.y + p.height + this.gap, p.width, canvas.height);
// Pipe cap
ctx.fillStyle = '#006400';
ctx.fillRect(p.x - 5, p.y + p.height + this.gap, p.width + 10, 20);
}
},
update: function() {
if (gameState !== "playing") return;
if (frames % 100 === 0) {
this.position.push({
x: canvas.width,
y: this.maxYPos * (Math.random() + 1),
width: 60,
height: 300
});
}
for (let i = 0; i < this.position.length; i++) {
let p = this.position[i];
// Move pipe to the left
p.x -= this.dx;
// If pipe moves off screen, remove it
if (p.x + p.width <= 0) {
this.position.shift();
pipesPassed++;
pipesPassedDisplay.textContent = pipesPassed;
}
// Collision detection
// 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
) {
gameOver();
}
// 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
) {
gameOver();
}
// Score when passing a pipe
if (p.x + p.width < bird.x && !p.passed) {
score++;
scoreDisplay.textContent = score;
p.passed = true;
}
}
},
reset: function() {
this.position = [];
}
};
// Background elements
const background = {
draw: function() {
// Sky
ctx.fillStyle = '#70c5ce';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Clouds
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
ctx.beginPath();
ctx.arc(100, 80, 30, 0, Math.PI * 2);
ctx.arc(130, 70, 35, 0, Math.PI * 2);
ctx.arc(160, 80, 25, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(300, 120, 30, 0, Math.PI * 2);
ctx.arc(330, 110, 35, 0, Math.PI * 2);
ctx.arc(360, 120, 25, 0, Math.PI * 2);
ctx.fill();
}
};
// Game functions
function gameOver() {
gameState = "gameover";
finalScore.textContent = score;
finalPipes.textContent = pipesPassed;
gameOverScreen.style.display = "block";
}
function resetGame() {
score = 0;
pipesPassed = 0;
frames = 0;
scoreDisplay.textContent = score;
pipesPassedDisplay.textContent = pipesPassed;
bird.reset();
pipes.reset();
gameOverScreen.style.display = "none";
gameState = "playing";
}
// Draw everything
function draw() {
background.draw();
pipes.draw();
drawGround();
bird.draw();
}
// Update game state
function update() {
bird.update();
pipes.update();
}
// Game loop
function loop() {
update();
draw();
frames++;
requestAnimationFrame(loop);
}
// Event listeners
function flap() {
if (gameState === "start") {
gameState = "playing";
}
if (gameState === "playing") {
bird.flap();
}
}
canvas.addEventListener("click", flap);
document.addEventListener("keydown", function(e) {
if (e.code === "Space") {
flap();
}
});
restartButton.addEventListener("click", resetGame);
// Touch support for mobile devices
canvas.addEventListener("touchstart", function(e) {
e.preventDefault();
flap();
});
// Start the game
loop();
</script>
</body>
</html>