Untitled
unknown
plain_text
9 months ago
8.4 kB
13
Indexable
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Simple Flappy Bird</title>
<style>
body {
margin: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #333; /* Dark background for visibility */
font-family: sans-serif;
}
canvas {
border: 2px solid #000;
background-color: #70c5ce; /* Light blue sky */
display: block; /* Remove default canvas bottom margin */
}
#instructions {
position: absolute;
top: 20px;
color: white;
padding: 10px;
background: rgba(0, 0, 0, 0.5);
border-radius: 5px;
}
</style>
</head>
<body>
<div id="instructions">
Click or press SPACE to flap!
</div>
<canvas id="gameCanvas" width="500" height="800"></canvas>
<script>
// --- 1. Setup Canvas and Context ---
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// --- 2. Game Constants ---
const CANVAS_WIDTH = canvas.width;
const CANVAS_HEIGHT = canvas.height;
const PIPE_GAP = 200; // Vertical space between top and bottom pipe
const PIPE_SPEED = 2; // How fast pipes move left
const PIPE_WIDTH = 50;
const GRAVITY = 0.5;
const FLAP_VELOCITY = -10;
const PIPE_SPAWN_RATE = 1500; // Spawn new pipe every 1500ms
// --- 3. Game State Variables ---
let bird;
let pipes = [];
let score = 0;
let isGameOver = false;
let lastPipeTime = 0;
// --- 4. Game Objects ---
// Bird object
class Bird {
constructor() {
this.x = CANVAS_WIDTH / 4;
this.y = CANVAS_HEIGHT / 2;
this.radius = 20;
this.velocity = 0;
}
update() {
// Apply gravity
this.velocity += GRAVITY;
this.y += this.velocity;
// Limit velocity (optional: to prevent rocket-speed crashes)
if (this.velocity > 15) this.velocity = 15;
}
draw() {
ctx.fillStyle = 'yellow';
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fill();
}
flap() {
if (!isGameOver) {
this.velocity = FLAP_VELOCITY;
}
}
}
// Pipe object
class Pipe {
constructor(x) {
this.x = x;
this.width = PIPE_WIDTH;
// Random height for the top pipe
this.topHeight = Math.floor(Math.random() * (CANVAS_HEIGHT - 300 - PIPE_GAP)) + 100;
this.bottomY = this.topHeight + PIPE_GAP;
this.bottomHeight = CANVAS_HEIGHT - this.bottomY;
this.passed = false; // Flag for scoring
}
update() {
this.x -= PIPE_SPEED;
}
draw() {
ctx.fillStyle = 'green';
// Top pipe
ctx.fillRect(this.x, 0, this.width, this.topHeight);
// Bottom pipe
ctx.fillRect(this.x, this.bottomY, this.width, this.bottomHeight);
}
}
// --- 5. Game Functions ---
function spawnPipe() {
pipes.push(new Pipe(CANVAS_WIDTH));
}
function checkCollision() {
// 1. Ground/Roof collision
if (bird.y + bird.radius >= CANVAS_HEIGHT || bird.y - bird.radius <= 0) {
gameOver();
return;
}
// 2. Pipe collision
for (let i = 0; i < pipes.length; i++) {
const p = pipes[i];
// Check if bird is horizontally aligned with the pipe
if (bird.x + bird.radius > p.x && bird.x - bird.radius < p.x + p.width) {
// Check collision with the top pipe
if (bird.y - bird.radius < p.topHeight) {
gameOver();
return;
}
// Check collision with the bottom pipe
if (bird.y + bird.radius > p.bottomY) {
gameOver();
return;
}
}
}
}
function updateScore() {
for (let i = 0; i < pipes.length; i++) {
const p = pipes[i];
// If the bird has passed the pipe's center and hasn't been scored yet
if (p.x + p.width < bird.x - bird.radius && !p.passed) {
score++;
p.passed = true;
}
}
}
function drawScore() {
ctx.fillStyle = 'white';
ctx.font = '48px Arial';
ctx.textAlign = 'center';
ctx.fillText(score, CANVAS_WIDTH / 2, 60);
}
function gameOver() {
isGameOver = true;
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
ctx.fillStyle = 'white';
ctx.font = '60px Arial';
ctx.fillText("GAME OVER", CANVAS_WIDTH / 2, CANVAS_HEIGHT / 2 - 40);
ctx.font = '30px Arial';
ctx.fillText("Click or press SPACE to restart", CANVAS_WIDTH / 2, CANVAS_HEIGHT / 2 + 30);
}
function resetGame() {
bird = new Bird();
pipes = [];
score = 0;
isGameOver = false;
lastPipeTime = 0;
// Spawn the first pipe immediately to start the game flow
spawnPipe();
gameLoop(0); // Restart the game loop
}
// --- 6. The Main Game Loop (Animation) ---
function gameLoop(timestamp) {
// 1. Clear the canvas
ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
if (!isGameOver) {
// 2. Update and Draw Pipes
if (timestamp - lastPipeTime > PIPE_SPAWN_RATE) {
spawnPipe();
lastPipeTime = timestamp;
}
// Filter out pipes that have gone off-screen and update the rest
pipes = pipes.filter(p => p.x + p.width > 0);
pipes.forEach(p => {
p.update();
p.draw();
});
// 3. Update and Draw Bird
bird.update();
bird.draw();
// 4. Check State
checkCollision();
updateScore();
// 5. Draw Score
drawScore();
// 6. Request next frame
requestAnimationFrame(gameLoop);
} else {
gameOver();
}
}
// --- 7. Event Listeners ---
function handleInput() {
if (isGameOver) {
resetGame();
} else {
bird.flap();
}
}
// Handle mouse/touch clicks
canvas.addEventListener('mousedown', handleInput);
canvas.addEventListener('touchstart', handleInput);
// Handle spacebar press
document.addEventListener('keydown', (e) => {
if (e.code === 'Space') {
e.preventDefault(); // Prevent scrolling when space is pressed
handleInput();
}
});
// --- 8. Start the Game ---
resetGame(); // Initializes game components and starts the loop
</script>
</body>
</html>
Editor is loading...
Leave a Comment