Creation chat
ClarkZhang
do you know flappy bird?
02-25 03:47
暂无回复
No comments yet
登录 后可以评论
Assistant
Yes, I know Flappy Bird! It's a popular mobile game where you tap to make a bird fly upward through gaps between pipes. I can create a Flappy Bird clone for you using HTML5 Canvas and JavaScript. Here's a complete implementation:
(HTML code has been generated and updated; see above for the current version.)
This Flappy Bird clone includes:
1. **Modern UI with Tailwind CSS** - Using the "现代蓝" color palette as requested
2. **Responsive design** - Works on both mobile and desktop
3. **Complete game mechanics**:
- Bird physics with gravity and jumping
- Randomly generated pipes
- Collision detection
- Score tracking
- Best score persistence with localStorage
4. **Game states**:
- Start screen
- Active gameplay
- Game over screen with restart option
5. **Controls**:
- Tap/click to make the bird jump
- Keyboard support (space/up arrow)
- Touch support with proper event handling
6. **Visual design**:
- Clean, colorful graphics
- Smooth animations
- Dark/light mode support
- Responsive layout
The game is contained in a fixed-height canvas (70vh with max-height of 600px) to ensure visibility in iframes. All game elements are drawn using Canvas 2D API with simple shapes and colors - no external images.
To play:
1. Click the "Start Game" button
2. Tap/click or press space/up arrow to make the bird fly upward
3. Navigate through the pipes without hitting them
4. Try to beat your high score!
The game saves your best score in localStorage so it persists between sessions. You can toggle between light and dark mode using the theme button in the top-right corner.
Console logs are available in the bottom-right preview panel if you need to debug anything.
▼ Code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flappy Bird Clone</title>
<script src="https://cdn.tailwindcss.com"></script>
<script>tailwind.config = { darkMode: 'class' }</script>
<style>
canvas {
touch-action: none;
}
</style>
</head>
<body class="min-h-screen bg-slate-50 dark:bg-slate-900 text-slate-900 dark:text-slate-100 flex flex-col items-center justify-center p-4">
<!-- Theme toggle button -->
<button id="theme-toggle-btn" class="fixed top-4 right-4 z-10 p-2 rounded-md hover:bg-gray-200 dark:hover:bg-gray-700 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 transition-colors flex items-center justify-center" aria-label="Toggle theme" onclick="toggleTheme()">
<svg id="icon-moon" class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M21.752 15.002A9.72 9.72 0 0 1 18 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 0 0 3 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 0 0 9.002-5.998z"/></svg>
<svg id="icon-sun" class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24" aria-hidden="true" style="display:none"><circle cx="12" cy="12" r="4"></circle><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"></path></svg>
</button>
<script>
function toggleTheme(){ var el=document.documentElement; var isDark=!el.classList.contains('dark'); el.classList.toggle('dark',isDark); localStorage.setItem('theme',isDark?'dark':'light'); applyTheme(isDark); }
function applyTheme(isDark){ document.getElementById('icon-moon').style.display=isDark?'none':'block'; document.getElementById('icon-sun').style.display=isDark?'block':'none'; }
document.addEventListener('DOMContentLoaded',function(){ var saved=localStorage.getItem('theme'); var isDark=saved==='dark'||(!saved&&window.matchMedia('(prefers-color-scheme: dark)').matches); document.documentElement.classList.toggle('dark',isDark); applyTheme(isDark); });
</script>
<div class="w-full max-w-2xl flex flex-col items-center">
<h1 class="text-3xl font-bold mb-2 text-slate-900 dark:text-slate-100">Flappy Bird Clone</h1>
<p class="mb-6 text-slate-700 dark:text-slate-300">Tap or click to fly through the pipes!</p>
<div class="relative w-full mb-6">
<canvas id="gameCanvas" class="w-full h-[70vh] max-h-[600px] bg-sky-100 dark:bg-slate-800 rounded-xl shadow-lg border-2 border-slate-200 dark:border-slate-700" width="400" height="600"></canvas>
<div id="startScreen" class="absolute inset-0 flex flex-col items-center justify-center bg-black bg-opacity-50 rounded-xl">
<h2 class="text-2xl font-bold text-white mb-4">Flappy Bird</h2>
<p class="text-white mb-2">Tap or click to start</p>
<p class="text-white mb-6">Tap/click to fly upward</p>
<button id="startButton" class="px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-bold rounded-full transition-colors">Start Game</button>
</div>
<div id="gameOverScreen" class="absolute inset-0 flex flex-col items-center justify-center bg-black bg-opacity-50 rounded-xl hidden">
<h2 class="text-2xl font-bold text-white mb-2">Game Over!</h2>
<p id="finalScore" class="text-xl text-white mb-4">Score: 0</p>
<button id="restartButton" class="px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-bold rounded-full transition-colors">Play Again</button>
</div>
</div>
<div class="w-full flex justify-between items-center">
<div class="text-lg font-semibold">
Score: <span id="scoreDisplay">0</span>
</div>
<div class="text-lg font-semibold">
Best: <span id="bestScoreDisplay">0</span>
</div>
</div>
</div>
<script>
// Game elements
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const startScreen = document.getElementById('startScreen');
const gameOverScreen = document.getElementById('gameOverScreen');
const startButton = document.getElementById('startButton');
const restartButton = document.getElementById('restartButton');
const scoreDisplay = document.getElementById('scoreDisplay');
const bestScoreDisplay = document.getElementById('bestScoreDisplay');
const finalScore = document.getElementById('finalScore');
// Game variables
let bird, pipes, score, bestScore, gameRunning, frames;
// Load best score from localStorage
bestScore = localStorage.getItem('flappyBestScore') || 0;
bestScoreDisplay.textContent = bestScore;
// Bird properties
const birdProps = {
x: 50,
y: canvas.height / 2 - 10,
width: 30,
height: 30,
gravity: 0.5,
velocity: 0,
jump: -10
};
// Pipe properties
const pipeProps = {
width: 60,
gap: 150,
minHeight: 50,
maxHeight: 300,
speed: 3
};
// Initialize game
function init() {
bird = { ...birdProps };
pipes = [];
score = 0;
frames = 0;
gameRunning = false;
scoreDisplay.textContent = score;
startScreen.classList.remove('hidden');
gameOverScreen.classList.add('hidden');
}
// Draw bird
function drawBird() {
ctx.fillStyle = '#f59e0b'; // amber-500
ctx.beginPath();
ctx.arc(bird.x, bird.y, bird.width / 2, 0, Math.PI * 2);
ctx.fill();
// Draw eye
ctx.fillStyle = '#000';
ctx.beginPath();
ctx.arc(bird.x + 8, bird.y - 5, 4, 0, Math.PI * 2);
ctx.fill();
// Draw beak
ctx.fillStyle = '#f97316'; // orange-500
ctx.beginPath();
ctx.moveTo(bird.x + 15, bird.y);
ctx.lineTo(bird.x + 25, bird.y);
ctx.lineTo(bird.x + 15, bird.y + 5);
ctx.fill();
}
// Draw pipes
function drawPipes() {
pipes.forEach(pipe => {
// Top pipe
ctx.fillStyle = '#10b981'; // emerald-500
ctx.fillRect(pipe.x, 0, pipe.width, pipe.topHeight);
// Pipe cap
ctx.fillStyle = '#059669'; // emerald-600
ctx.fillRect(pipe.x - 3, pipe.topHeight - 15, pipe.width + 6, 15);
// Bottom pipe
ctx.fillStyle = '#10b981'; // emerald-500
ctx.fillRect(pipe.x, pipe.topHeight + pipe.gap, pipe.width, canvas.height);
// Pipe cap
ctx.fillStyle = '#059669'; // emerald-600
ctx.fillRect(pipe.x - 3, pipe.topHeight + pipe.gap, pipe.width + 6, 15);
});
}
// Draw background
function drawBackground() {
// Sky
ctx.fillStyle = '#dbeafe'; // blue-100
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Ground
ctx.fillStyle = '#a16207'; // amber-800
ctx.fillRect(0, canvas.height - 20, canvas.width, 20);
// Grass
ctx.fillStyle = '#65a30d'; // lime-600
ctx.fillRect(0, canvas.height - 20, canvas.width, 5);
}
// Update bird position
function updateBird() {
bird.velocity += bird.gravity;
bird.y += bird.velocity;
// Floor collision
if (bird.y + bird.height / 2 >= canvas.height - 20) {
bird.y = canvas.height - 20 - bird.height / 2;
if (gameRunning) endGame();
}
// Ceiling collision
if (bird.y - bird.height / 2 <= 0) {
bird.y = bird.height / 2;
bird.velocity = 0;
}
}
// Update pipes
function updatePipes() {
// Add new pipe every 100 frames
if (frames % 100 === 0) {
const topHeight = Math.floor(Math.random() * (pipeProps.maxHeight - pipeProps.minHeight)) + pipeProps.minHeight;
pipes.push({
x: canvas.width,
topHeight: topHeight,
gap: pipeProps.gap,
width: pipeProps.width,
passed: false
});
}
// Move pipes and check collisions
for (let i = 0; i < pipes.length; i++) {
const pipe = pipes[i];
pipe.x -= pipeProps.speed;
// Remove pipes that are off screen
if (pipe.x + pipe.width < 0) {
pipes.splice(i, 1);
i--;
continue;
}
// Check if bird passed the pipe
if (!pipe.passed && pipe.x + pipe.width < bird.x) {
pipe.passed = true;
score++;
scoreDisplay.textContent = score;
}
// Collision detection
if (
bird.x + bird.width / 2 > pipe.x &&
bird.x - bird.width / 2 < pipe.x + pipe.width &&
(bird.y - bird.height / 2 < pipe.topHeight ||
bird.y + bird.height / 2 > pipe.topHeight + pipe.gap)
) {
endGame();
}
}
}
// Game loop
function gameLoop() {
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw game elements
drawBackground();
drawPipes();
drawBird();
if (gameRunning) {
updateBird();
updatePipes();
frames++;
requestAnimationFrame(gameLoop);
}
}
// Make bird jump
function jump() {
if (!gameRunning) return;
bird.velocity = bird.jump;
}
// Start game
function startGame() {
gameRunning = true;
startScreen.classList.add('hidden');
gameLoop();
}
// End game
function endGame() {
gameRunning = false;
// Update best score
if (score > bestScore) {
bestScore = score;
localStorage.setItem('flappyBestScore', bestScore);
bestScoreDisplay.textContent = bestScore;
}
// Show game over screen
finalScore.textContent = `Score: ${score}`;
gameOverScreen.classList.remove('hidden');
}
// Event listeners
canvas.addEventListener('click', () => {
if (gameRunning) {
jump();
}
});
canvas.addEventListener('touchstart', (e) => {
e.preventDefault();
if (gameRunning) {
jump();
}
}, { passive: false });
startButton.addEventListener('click', startGame);
restartButton.addEventListener('click', init);
// Keyboard support
document.addEventListener('keydown', (e) => {
if (e.code === 'Space' || e.code === 'ArrowUp') {
if (gameRunning) {
jump();
}
}
});
// Initialize game
init();
</script>
<div class="mt-8 text-center text-sm text-slate-600 dark:text-slate-400">
<p>Tip: Use spacebar or arrow up key for keyboard control</p>
<p class="mt-2">Console logs available in the bottom-right preview panel</p>
</div>
</body>
</html>
02-25 03:49
暂无回复
No comments yet
登录 后可以评论
Comments
暂无回复
No comments yet
登录 后可以评论