Flappy Bird Clone
by CrystalEagle79240 lines5.3 KB
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Flappy Bird Clone</title>
<style>
html, body {
margin: 0;
padding: 0;
background: #4ec0ca;
overflow: hidden;
font-family: sans-serif;
touch-action: manipulation;
}
canvas {
display: block;
margin: 0 auto;
background: #4ec0ca;
}
#ui {
position: fixed;
top: 10px;
left: 50%;
transform: translateX(-50%);
color: #fff;
text-shadow: 2px 2px 0 #000;
font-size: 24px;
pointer-events: none;
text-align: center;
}
</style>
</head>
<body>
<div id="ui">Click or tap to flap<br>Score: <span id="score">0</span></div>
<canvas id="game"></canvas>
<script>
const canvas = document.getElementById("game");
const ctx = canvas.getContext("2d");
const uiScore = document.getElementById("score");
function resize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
resize();
window.addEventListener("resize", resize);
const GRAVITY = 0.4;
const FLAP = -7;
const PIPE_GAP = 160;
const PIPE_WIDTH = 70;
const PIPE_SPEED = 3;
let bird = {
x: canvas.width * 0.3,
y: canvas.height * 0.5,
vy: 0,
r: 18
};
let pipes = [];
let score = 0;
let gameOver = false;
let started = false;
function resetGame() {
bird.x = canvas.width * 0.3;
bird.y = canvas.height * 0.5;
bird.vy = 0;
pipes = [];
score = 0;
gameOver = false;
started = false;
uiScore.textContent = score;
}
function addPipe() {
const center = Math.random() * (canvas.height * 0.6) + canvas.height * 0.2;
pipes.push({
x: canvas.width + PIPE_WIDTH,
top: center - PIPE_GAP / 2,
bottom: center + PIPE_GAP / 2,
passed: false
});
}
function flap() {
if (!started) {
started = true;
}
if (gameOver) {
resetGame();
return;
}
bird.vy = FLAP;
}
window.addEventListener("mousedown", flap);
window.addEventListener("touchstart", function(e) {
e.preventDefault();
flap();
}, { passive: false });
let pipeTimer = 0;
const PIPE_INTERVAL = 120; // frames
function update() {
if (!started) {
draw();
requestAnimationFrame(update);
return;
}
if (!gameOver) {
bird.vy += GRAVITY;
bird.y += bird.vy;
pipeTimer++;
if (pipeTimer >= PIPE_INTERVAL) {
pipeTimer = 0;
addPipe();
}
for (let i = pipes.length - 1; i >= 0; i--) {
const p = pipes[i];
p.x -= PIPE_SPEED;
// score
if (!p.passed && p.x + PIPE_WIDTH < bird.x) {
p.passed = true;
score++;
uiScore.textContent = score;
}
// remove off-screen
if (p.x + PIPE_WIDTH < 0) {
pipes.splice(i, 1);
}
}
// collisions
if (bird.y - bird.r < 0 || bird.y + bird.r > canvas.height) {
gameOver = true;
}
for (const p of pipes) {
if (
bird.x + bird.r > p.x &&
bird.x - bird.r < p.x + PIPE_WIDTH
) {
if (bird.y - bird.r < p.top || bird.y + bird.r > p.bottom) {
gameOver = true;
}
}
}
}
draw();
requestAnimationFrame(update);
}
function drawBird() {
ctx.save();
ctx.translate(bird.x, bird.y);
ctx.fillStyle = "#ffeb3b";
ctx.beginPath();
ctx.arc(0, 0, bird.r, 0, Math.PI * 2);
ctx.fill();
// eye
ctx.fillStyle = "#fff";
ctx.beginPath();
ctx.arc(bird.r * 0.3, -bird.r * 0.3, bird.r * 0.35, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = "#000";
ctx.beginPath();
ctx.arc(bird.r * 0.45, -bird.r * 0.3, bird.r * 0.15, 0, Math.PI * 2);
ctx.fill();
// beak
ctx.fillStyle = "#ff9800";
ctx.beginPath();
ctx.moveTo(bird.r * 0.8, 0);
ctx.lineTo(bird.r * 1.3, -bird.r * 0.2);
ctx.lineTo(bird.r * 1.3, bird.r * 0.2);
ctx.closePath();
ctx.fill();
ctx.restore();
}
function drawPipes() {
ctx.fillStyle = "#5fb34a";
for (const p of pipes) {
// top pipe
ctx.fillRect(p.x, 0, PIPE_WIDTH, p.top);
// bottom pipe
ctx.fillRect(p.x, p.bottom, PIPE_WIDTH, canvas.height - p.bottom);
}
}
function drawGround() {
ctx.fillStyle = "#ded895";
ctx.fillRect(0, canvas.height - 40, canvas.width, 40);
}
function drawText() {
ctx.fillStyle = "#fff";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.font = "32px sans-serif";
if (!started && !gameOver) {
ctx.fillText("Click or tap to start", canvas.width / 2, canvas.height / 2);
} else if (gameOver) {
ctx.fillText("Game Over", canvas.width / 2, canvas.height / 2 - 30);
ctx.font = "24px sans-serif";
ctx.fillText("Click or tap to restart", canvas.width / 2, canvas.height / 2 + 10);
}
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// sky
ctx.fillStyle = "#4ec0ca";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawPipes();
drawGround();
drawBird();
drawText();
}
resetGame();
update();
</script>
</body>
</html>
Game Source: Flappy Bird Clone
Creator: CrystalEagle79
Libraries: none
Complexity: complex (240 lines, 5.3 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: flappy-bird-clone-crystaleagle79" to link back to the original. Then publish at arcadelab.ai/publish.