
Viral LLM Coding Challenges
Prompt
Using React, create a clone of the Flappy Bird game, including gravity, obstacles (pipes), scoring, and collision detection.
A system prompt was added to support web rendering
```html <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Flappy Bird Clone</title> <style> body { margin: 0; background: #d0f4f7; display: flex; align-items: center; justify-content: center; height: 100vh; user-select: none; overflow: hidden; } .game { position: relative; width: 400px; height: 600px; background: #70c5ce; border: 2px solid #000; overflow: hidden; } .bird { position: absolute; width: 34px; height: 24px; background: yellow; border-radius: 50%; } .pipe { position: absolute; width: 60px; background: green; } .score { position: absolute; top: 10px; left: 50%; transform: translateX(-50%); font: bold 24px sans-serif; color: #fff; text-shadow: 2px 2px #000; } .game-over { position: absolute; width: 100%; height: 100%; background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; flex-direction: column; color: white; font: bold 32px sans-serif; } .game-over div + div { font-size: 20px; margin-top: 10px; } </style> <!-- React and Babel --> <script src="https://unpkg.com/react@17/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script> <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> </head> <body> <div id="root"></div> <script type="text/babel"> class FlappyBird extends React.Component { constructor(props) { super(props); this.gameWidth = 400; this.gameHeight = 600; this.birdX = 80; this.birdSize = 34; this.gravity = 0.6; this.jumpVelocity = -10; this.pipeWidth = 60; this.pipeGap = 150; this.pipeInterval = 90; // frames between pipes this.pipeSpeed = 2; this.state = { birdY: this.gameHeight / 2, velocity: 0, pipes: [], frameCount: 0, score: 0, gameOver: false }; } componentDidMount() { document.addEventListener('keydown', this.handleInput); document.addEventListener('mousedown', this.handleInput); this.interval = setInterval(this.gameLoop, 20); } componentWillUnmount() { clearInterval(this.interval); document.removeEventListener('keydown', this.handleInput); document.removeEventListener('mousedown', this.handleInput); } handleInput = (e) => { if (!this.state.gameOver && (e.key === ' ' || e.type === 'mousedown')) { this.setState({ velocity: this.jumpVelocity }); } else if (this.state.gameOver) { this.resetGame(); } }; gameLoop = () => { let { birdY, velocity, pipes, frameCount, score, gameOver } = this.state; if (gameOver) return; // Bird physics velocity += this.gravity; birdY += velocity; // Add new pipe if (frameCount % this.pipeInterval === 0) { const topHeight = Math.random() * (this.gameHeight - this.pipeGap - 100) + 50; pipes.push({ x: this.gameWidth, top: topHeight, passed: false }); } // Move pipes const newPipes = pipes.map(pipe => ({ ...pipe, x: pipe.x - this.pipeSpeed })); // Remove off-screen pipes while (newPipes.length && newPipes[0].x < -this.pipeWidth) { newPipes.shift(); } // Update score newPipes.forEach(pipe => { if (!pipe.passed && pipe.x + this.pipeWidth < this.birdX) { score++; pipe.passed = true; } }); // Collision detection if (birdY < 0 || birdY + this.birdSize > this.gameHeight) { gameOver = true; } newPipes.forEach(pipe => { if (this.isColliding(birdY, pipe)) { gameOver = true; } }); if (gameOver) { clearInterval(this.interval); } this.setState({ birdY, velocity, pipes: newPipes, frameCount: frameCount + 1, score, gameOver }); }; isColliding(birdY, pipe) { const bx = this.birdX; const by = birdY; const bw = this.birdSize; const bh = this.birdSize; const px = pipe.x; const pw = this.pipeWidth; const topH = pipe.top; const bottomY = topH + this.pipeGap; if (bx + bw > px && bx < px + pw) { if (by < topH || by + bh > bottomY) { return true; } } return false; } resetGame() { this.setState({ birdY: this.gameHeight / 2, velocity: 0, pipes: [], frameCount: 0, score: 0, gameOver: false }, () => { this.interval = setInterval(this.gameLoop, 20); }); } render() { const { birdY, pipes, score, gameOver } = this.state; return ( <div className="game"> <div className="bird" style={{ top: birdY, left: this.birdX }} /> {pipes.map((pipe, i) => ( <React.Fragment key={i}> <div className="pipe" style={{ height: pipe.top, top: 0, left: pipe.x, width: this.pipeWidth }} /> <div className="pipe" style={{ height: this.gameHeight - pipe.top - this.pipeGap, top: pipe.top + this.pipeGap, left: pipe.x, width: this.pipeWidth }} /> </React.Fragment> ))} <div className="score">{score}</div> {gameOver && ( <div className="game-over"> <div>Game Over</div> <div>Click or Press Space to Restart</div> </div> )} </div> ); } } ReactDOM.render(<FlappyBird />, document.getElementById('root')); </script> </body> </html> ```
Drag to resize
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>React Flappy Bird</title> <style> body { margin: 0; padding: 0; display: flex; justify-content: center; align-items: center; min-height: 100vh; background-color: #333; font-family: 'Arial', sans-serif; } </style> </head> <body> <div id="root"></div> <!-- React and Babel Libraries --> <script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script> <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script> <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> <!-- React Application --> <script type="text/babel"> const { useState, useEffect, useCallback, useRef } = React; // --- Game Constants --- const GAME_WIDTH = 500; const GAME_HEIGHT = 600; const BIRD_WIDTH = 50; const BIRD_HEIGHT = 40; const BIRD_LEFT = GAME_WIDTH / 4; const GRAVITY = 0.6; const JUMP_STRENGTH = -11; const PIPE_WIDTH = 80; const PIPE_GAP = 200; const PIPE_SPEED = 4; const PIPE_INTERVAL = 1500; // time in ms between pipe spawns // --- Reusable Styled Components --- const GameBox = ({ children }) => ( <div style={{ position: 'relative', width: `${GAME_WIDTH}px`, height: `${GAME_HEIGHT}px`, backgroundColor: '#87CEEB', // Sky blue overflow: 'hidden', border: '2px solid black', }}> {children} </div> ); const Bird = ({ birdY }) => ( <div style={{ position: 'absolute', backgroundColor: 'yellow', width: `${BIRD_WIDTH}px`, height: `${BIRD_HEIGHT}px`, borderRadius: '50%', border: '3px solid black', top: `${birdY}px`, left: `${BIRD_LEFT}px`, }} /> ); const Pipe = ({ x, topHeight }) => ( <> {/* Top Pipe */} <div style={{ position: 'absolute', backgroundColor: '#008000', // Green border: '3px solid black', width: `${PIPE_WIDTH}px`, height: `${topHeight}px`, left: `${x}px`, top: 0, }} /> {/* Bottom Pipe */} <div style={{ position: 'absolute', backgroundColor: '#008000', // Green border: '3px solid black', width: `${PIPE_WIDTH}px`, height: `${GAME_HEIGHT - topHeight - PIPE_GAP}px`, left: `${x}px`, bottom: 0, }} /> </> ); const ScoreDisplay = ({ score }) => ( <div style={{ position: 'absolute', top: '20px', left: '50%', transform: 'translateX(-50%)', color: 'white', fontSize: '48px', fontWeight: 'bold', textShadow: '2px 2px 4px black', zIndex: 10, }}> {score} </div> ); const Overlay = ({ gameState, score, onRestart }) => { if (gameState === 'playing') return null; const message = gameState === 'menu' ? 'Click to Start' : `Game Over! Score: ${score}`; const buttonText = gameState === 'menu' ? 'Start' : 'Restart'; return ( <div style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0, 0, 0, 0.5)', display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', color: 'white', zIndex: 20 }}> <h1 style={{ fontSize: '4rem', margin: 0, textShadow: '3px 3px 6px black' }}>{message}</h1> {gameState === 'over' && ( <button onClick={onRestart} style={{ marginTop: '20px', padding: '15px 30px', fontSize: '1.5rem', cursor: 'pointer', borderRadius: '10px', border: 'none', backgroundColor: '#4CAF50', color: 'white' }}> {buttonText} </button> )} </div> ); }; // --- Main Game Component --- function FlappyBirdGame() { const [gameState, setGameState] = useState('menu'); // 'menu', 'playing', 'over' const [birdY, setBirdY] = useState(GAME_HEIGHT / 2 - BIRD_HEIGHT / 2); const [birdVelocity, setBirdVelocity] = useState(0); const [pipes, setPipes] = useState([]); const [score, setScore] = useState(0); const gameLoopRef = useRef(); const pipeTimerRef = useRef(); const resetGame = useCallback(() => { setGameState('playing'); setBirdY(GAME_HEIGHT / 2 - BIRD_HEIGHT / 2); setBirdVelocity(0); setPipes([]); setScore(0); }, []); // Game Loop useEffect(() => { if (gameState !== 'playing') { return; } const gameLoop = () => { // Gravity const newVelocity = birdVelocity + GRAVITY; const newBirdY = birdY + newVelocity; setBirdVelocity(newVelocity); setBirdY(newBirdY); // Move Pipes let scoreHasBeenUpdated = false; setPipes(currentPipes => { let newScore = score; const newPipes = currentPipes.map(pipe => { // Check for scoring if (!pipe.passed && pipe.x + PIPE_WIDTH < BIRD_LEFT) { newScore++; scoreHasBeenUpdated = true; return { ...pipe, x: pipe.x - PIPE_SPEED, passed: true }; } return { ...pipe, x: pipe.x - PIPE_SPEED }; }).filter(pipe => pipe.x > -PIPE_WIDTH); // Remove off-screen pipes if (scoreHasBeenUpdated) { setScore(newScore); } return newPipes; }); // Collision Detection // 1. Ground/Ceiling collision if (newBirdY > GAME_HEIGHT - BIRD_HEIGHT || newBirdY < 0) { setGameState('over'); } // 2. Pipe collision const currentPipe = pipes.find(pipe => pipe.x < BIRD_LEFT + BIRD_WIDTH && pipe.x + PIPE_WIDTH > BIRD_LEFT ); if (currentPipe) { const hasCollidedWithTop = newBirdY < currentPipe.topHeight; const hasCollidedWithBottom = newBirdY + BIRD_HEIGHT > currentPipe.topHeight + PIPE_GAP; if (hasCollidedWithTop || hasCollidedWithBottom) { setGameState('over'); } } gameLoopRef.current = requestAnimationFrame(gameLoop); }; gameLoopRef.current = requestAnimationFrame(gameLoop); return () => cancelAnimationFrame(gameLoopRef.current); }, [gameState, birdY, birdVelocity, pipes, score]); // Pipe Spawner useEffect(() => { if (gameState !== 'playing') { return; } pipeTimerRef.current = setInterval(() => { const topHeight = Math.floor(Math.random() * (GAME_HEIGHT - PIPE_GAP - 100)) + 50; // Random height, with 50px padding from top/bottom setPipes(currentPipes => [...currentPipes, { x: GAME_WIDTH, topHeight, passed: false }]); }, PIPE_INTERVAL); return () => clearInterval(pipeTimerRef.current); }, [gameState]); const handleInteraction = () => { if (gameState === 'menu') { resetGame(); } if (gameState === 'playing') { setBirdVelocity(JUMP_STRENGTH); } }; return ( <div onClick={handleInteraction} tabIndex="0" style={{ outline: 'none' }}> <GameBox> {pipes.map((pipe, index) => ( <Pipe key={index} x={pipe.x} topHeight={pipe.topHeight} /> ))} <Bird birdY={birdY} /> <ScoreDisplay score={score} /> <Overlay gameState={gameState} score={score} onRestart={resetGame} /> </GameBox> </div> ); } ReactDOM.render(<FlappyBirdGame />, document.getElementById('root')); </script> </body> </html> ```
Drag to resize
```html <!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> <style> html, body { margin: 0; padding: 0; overflow: hidden; font-family: Arial, sans-serif; } #root { width: 100vw; height: 100vh; display: flex; justify-content: center; align-items: center; background-color: #70c5ce; } </style> </head> <body> <div id="root"></div> <script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script> <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script> <script> const { useState, useEffect, useRef, useReducer } = React; const initialState = { birdY: 150, velocity: 0, pipes: [], score: 0, gameOver: false, running: false, pipeGap: 120, pipeWidth: 60, pipeFrequency: 1500, lastPipeTime: 0, gravity: 0.1 }; const gravityForce = 0.1; function gameReducer(state, action) { switch(action.type) { case 'START_GAME': return { ...initialState, running: true, lastPipeTime: performance.now(), }; case 'FLAP': return { ...state, velocity: -3.5 }; case 'UPDATE': const now = performance.now(); const deltaTime = now - state.lastFrameTime; const updatedLastFrameTime = now; const velocity = state.velocity + state.gravity * deltaTime; const birdY = state.birdY + velocity * deltaTime; let pipes = state.pipes.map(pipe => ({ ...pipe, x: pipe.x - 1.5 * deltaTime / 10 })); const passedPipes = pipes.filter(pipe => pipe.x + state.pipeWidth <= 50); const newPasses = passedPipes.length > state.score ? passedPipes.length : state.score; const score = newPasses; // Remove pipes off-screen pipes = pipes.filter(pipe => pipe.x + state.pipeWidth > 0); const pipeGap = state.pipeGap; const addPipe = now - state.lastPipeTime >= state.pipeFrequency; if (addPipe) { pipes.push({ x: window.innerWidth || 800, gap: (Math.random() * 150) + 50 }); } // Collision detection let collision = false; for (const pipe of pipes) { const birdLeft = 50; const birdRight = birdLeft + 20; const birdTop = birdY; const birdBottom = birdTop + 20; const pipeLeft = pipe.x; const pipeRight = pipe.x + state.pipeWidth; const pipeTopBottom = pipe.gap; const pipeBottomTop = pipe.gap + pipeGap; // Check if bird is in horizontally aligned to pipe if (birdRight > pipeLeft && birdLeft < pipeRight) { if (birdTop <= pipeTopBottom || birdBottom >= pipeBottomTop) { collision = true; break; } } } // Check if bird is above top or below ground if (birdY <= 0 || birdY + 20 >= 400) { collision = true; } return { ...state, birdY, velocity, pipes, score, gameOver: collision, running: !collision, lastPipeTime: addPipe ? now : state.lastPipeTime, lastFrameTime: updatedLastFrameTime }; default: return state; } } function FlappyBird() { const [state, dispatch] = useReducer(gameReducer, initialState); const canvasWidth = 800; const canvasHeight = 400; const animationId = useRef(null); const startGame = () => { dispatch({ type: 'START_GAME' }); dispatch({ type: 'UPDATE' }); animationId.current = requestAnimationFrame(gameLoop); }; const flap = () => { if (!state.running) { startGame(); } else { dispatch({ type: 'FLAP' }); } }; function gameLoop() { dispatch({ type: 'UPDATE' }); if (state.running) { animationId.current = requestAnimationFrame(gameLoop); } } useEffect(() => { const handleKeyDown = (e) => { if (e.code === "Space") { e.preventDefault(); flap(); } }; window.addEventListener('keydown', handleKeyDown); return () => { window.removeEventListener('keydown', handleKeyDown); cancelAnimationFrame(animationId.current); }; }, [state.running]); return ( <div style={{ position: 'relative', width: `${canvasWidth}px`, height: `${canvasHeight}px`, margin: 'auto', backgroundColor: '#70c5ce', overflow: 'hidden', borderRadius: '8px' }}> {/* Bird */} {!state.gameOver && ( <div onClick={flap} style={{ position: 'absolute', left: '50px', top: `${state.birdY}px`, width: '20px', height: '20px', backgroundColor: '#ff0', borderRadius: '50%', cursor: 'pointer' }} /> )} {/* Pipes */} {state.pipes.map((pipe, idx) => ( <React.Fragment key={idx}> {/* Top Pipe */} <div style={{ position: 'absolute', left: `${pipe.x}px`, top: '0px', width: `${state.pipeWidth}px`, height: `${pipe.gap}px`, backgroundColor: '#228b22' }} /> {/* Bottom Pipe */} <div style={{ position: 'absolute', left: `${pipe.x}px`, top: `${pipe.gap + state.pipeGap}px`, width: `${state.pipeWidth}px`, height: '500px', backgroundColor: '#228b22' }} /> </React.Fragment> ))} {/* Score */} <div style={{ position: 'absolute', top: '10px', left: '10px', fontSize: '24px', fontWeight: 'bold', color: 'white', textShadow: '1px 1px 2px black' }}> {state.score} </div> {/* Game Over */} {state.gameOver && !state.running && ( <div style={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', fontSize: '36px', fontWeight: 'bold', textAlign: 'center', color: '#fff', textShadow: '2px 2px 4px #000' }}> Game Over!<br/>Score: {state.score}<br/> Press Space to Restart </div> )} </div> ); } ReactDOM.render(<FlappyBird />, document.getElementById('root')); </script> </body> </html> ```
Drag to resize
# Flappy Bird Clone in React ## Overview In this example, we'll create a simplified version of the popular game Flappy Bird using React. The game will feature a bird that can be controlled with a single button (spacebar or mouse click), gravity pulling it down, pipes as obstacles, scoring, and collision detection. ## Code ```jsx import React, { useState, useEffect } from 'react'; // Game constants const GRAVITY = 0.2; const BIRD_SIZE = 30; const PIPE_WIDTH = 80; const PIPE_GAP = 120; const INITIAL_BIRD_Y = 200; const INITIAL_PIPES = [{ x: 400, y: 100 }, { x: 700, y: 250 }]; function App() { const [birdY, setBirdY] = useState(INITIAL_BIRD_Y); const [birdVelocity, setBirdVelocity] = useState(0); const [pipes, setPipes] = useState(INITIAL_PIPES); const [score, setScore] = useState(0); const [gameOver, setGameOver] = useState(false); // Handle user input (spacebar or mouse click) const handleInput = () => { if (!gameOver) { setBirdVelocity(-5); } else { resetGame(); } }; // Reset game state const resetGame = () => { setBirdY(INITIAL_BIRD_Y); setBirdVelocity(0); setPipes(INITIAL_PIPES); setScore(0); setGameOver(false); }; useEffect(() => { // Update bird position and velocity const intervalId = setInterval(() => { if (!gameOver) { setBirdVelocity(birdVelocity + GRAVITY); setBirdY(birdY + birdVelocity); // Check collision with ground or top if (birdY < 0 || birdY > window.innerHeight - BIRD_SIZE) { setGameOver(true); } } }, 16); // 60 FPS return () => clearInterval(intervalId); }, [birdY, birdVelocity, gameOver]); useEffect(() => { // Update pipes and check collisions const intervalId = setInterval(() => { if (!gameOver) { setPipes((prevPipes) => { const newPipes = prevPipes.map((pipe) => ({ ...pipe, x: pipe.x - 2 })); // Check if pipe has passed the bird if (newPipes[0].x < 50 && newPipes[0].x > 48) { setScore((prevScore) => prevScore + 1); } // Check collision with pipes if (newPipes.some((pipe) => { return (pipe.x < 50 + BIRD_SIZE && pipe.x + PIPE_WIDTH > 50) && (birdY < pipe.y || birdY + BIRD_SIZE > pipe.y + PIPE_GAP); })) { setGameOver(true); } // Remove off-screen pipes and add new ones if (newPipes[0].x < -PIPE_WIDTH) { newPipes.shift(); newPipes.push({ x: newPipes[newPipes.length - 1].x + 300, y: Math.random() * (window.innerHeight - PIPE_GAP) }); } return newPipes; }); } }, 16); // 60 FPS return () => clearInterval(intervalId); }, [pipes, birdY, gameOver]); // Handle user input events useEffect(() => { document.addEventListener('keydown', (e) => { if (e.key === ' ') { handleInput(); } }); document.addEventListener('click', handleInput); return () => { document.removeEventListener('keydown', handleInput); document.removeEventListener('click', handleInput); }; }, [handleInput]); return ( <div style={{ position: 'relative', width: '100%', height: '100vh', backgroundColor: '#87CEEB', overflow: 'hidden', }}> <div style={{ position: 'absolute', top: birdY, left: 50, width: BIRD_SIZE, height: BIRD_SIZE, backgroundColor: 'yellow', borderRadius: '50%', }} /> {pipes.map((pipe, index) => ( <div key={index} style={{ position: 'absolute', top: 0, left: pipe.x, width: PIPE_WIDTH, height: pipe.y, backgroundColor: 'green', }} /> ))} {pipes.map((pipe, index) => ( <div key={index + pipes.length} style={{ position: 'absolute', top: pipe.y + PIPE_GAP, left: pipe.x, width: PIPE_WIDTH, height: window.innerHeight, backgroundColor: 'green', }} /> ))} <div style={{ position: 'absolute', top: 20, left: 20, fontSize: 24, fontWeight: 'bold', }}> Score: {score} </div> {gameOver && ( <div style={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', fontSize: 36, fontWeight: 'bold', }}> Game Over! Click or press Space to restart. </div> )} </div> ); } export default App; ``` ## How to Run 1. Create a new React project using `npx create-react-app flappy-bird-clone`. 2. Replace the contents of `src/App.js` with the above code. 3. Start the development server with `npm start`. 4. Open your web browser and navigate to `http://localhost:3000`. ## Controls - **Spacebar** or **Mouse Click**: Flap the bird's wings to go up. If the game is over, restart the game. - The game automatically updates the score as you pass through the pipes. - The game ends when the bird collides with the ground, top, or pipes.
Drag to resize
# Flappy Bird React Clone I'll create a complete Flappy Bird clone using React with all the requested features, including physics, collision detection, pipes, and scoring. ```html <!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://unpkg.com/react@18/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script> <script src="https://unpkg.com/@babel/standalone@7.24.8/babel.min.js"></script> <style> * { margin: 0; padding: 0; box-sizing: border-box; user-select: none; } body { font-family: 'Arial Rounded MT Bold', 'Helvetica Rounded', Arial, sans-serif; display: flex; justify-content: center; align-items: center; min-height: 100vh; background: linear-gradient(to bottom, #1e5799, #207cca); overflow: hidden; touch-action: manipulation; } #root { display: flex; justify-content: center; align-items: center; width: 100%; height: 100%; } .game-container { position: relative; width: 360px; height: 640px; overflow: hidden; box-shadow: 0 0 30px rgba(0, 0, 0, 0.5); border-radius: 10px; } .game-background { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient(to bottom, #70c9e3 0%, #70c9e3 65%, #c9f1f9 65%, #c9f1f9 100%); overflow: hidden; } .scene { position: relative; width: 100%; height: 100%; overflow: hidden; } .sky { position: absolute; top: 0; left: 0; width: 100%; height: 75%; background: linear-gradient(to bottom, #70c9e3, #a2d9f0); } .cloud { position: absolute; background-color: white; border-radius: 50px; } .cloud:nth-child(1) { top: 20%; left: 30%; width: 60px; height: 20px; } .cloud:nth-child(2) { top: 40%; left: 60%; width: 80px; height: 25px; } .cloud:nth-child(3) { top: 15%; left: 70%; width: 70px; height: 22px; } .cloud:nth-child(4) { top: 35%; left: 20%; width: 90px; height: 30px; } .ground { position: absolute; bottom: 0; left: 0; width: 100%; height: 25%; background: linear-gradient(to top, #e3b86c, #d9a44a); border-top: 10px solid #a37c3e; } .grass { position: absolute; top: -25px; width: 100%; height: 30px; background: linear-gradient(to bottom, #71c837, #62a328); border-radius: 30px 30px 0 0; } .pipe { position: absolute; width: 70px; display: flex; flex-direction: column; align-items: center; } .pipe-top { height: 40px; width: 80px; background: linear-gradient(to right, #74c042, #5ca032); border: 4px solid #3a8020; border-bottom: none; border-radius: 10px 10px 0 0; z-index: 1; } .pipe-body { height: 300px; width: 70px; background: linear-gradient(to right, #5dbd2b, #4da029); border: 4px solid #3a8020; display: flex; justify-content: center; } .pipe.upper .pipe-body { flex-direction: column-reverse; } .pipe.lower .pipe-body { flex-direction: column; border-top: none; } .bird { position: absolute; width: 40px; height: 30px; z-index: 10; transition: transform 0.1s; } .bird-body { position: absolute; width: 40px; height: 30px; background: linear-gradient(45deg, #ffd900, #ffac00); border-radius: 50% 50% 40% 40%; top: 0; left: 0; } .bird-eye { position: absolute; width: 10px; height: 10px; background-color: white; border-radius: 50%; top: 8px; right: 8px; display: flex; justify-content: center; align-items: center; } .bird-pupil { width: 5px; height: 5px; background-color: black; border-radius: 50%; } .bird-wing { position: absolute; width: 30px; height: 15px; background: linear-gradient(to bottom, #ffd900, #e09800); border-radius: 50%; top: 15px; left: -5px; transform-origin: 70% 50%; } .bird-beak { position: absolute; width: 15px; height: 10px; background: linear-gradient(to right, #ff6600, #cc5500); border-radius: 50% 0 0 50%; top: 12px; right: -10px; transform-origin: left center; } .score-display { position: absolute; top: 50px; left: 0; width: 100%; text-align: center; font-size: 60px; font-weight: bold; color: white; text-shadow: 3px 3px 5px rgba(0, 0, 0, 0.5); z-index: 5; } .instructions { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 80%; max-width: 300px; background-color: rgba(255, 255, 255, 0.85); padding: 20px; border-radius: 15px; text-align: center; box-shadow: 0 0 20px rgba(0, 0, 0, 0.3); z-index: 20; } .instructions h2 { color: #ff5722; margin-bottom: 20px; font-size: 24px; } .controls { display: flex; justify-content: space-around; margin: 20px 0; } .control-item { display: flex; flex-direction: column; align-items: center; } .control-item span { font-size: 18px; font-weight: bold; margin-top: 5px; color: #333; } .key { width: 60px; height: 60px; background: linear-gradient(to bottom, #fff, #ccc); border-radius: 10px; display: flex; justify-content: center; align-items: center; font-size: 24px; color: #333; box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3); } .start-button { margin-top: 20px; padding: 12px 30px; font-size: 18px; font-weight: bold; background: linear-gradient(to bottom, #ff9800, #f57c00); border: none; border-radius: 30px; color: white; cursor: pointer; transition: all 0.2s; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); } .start-button:hover { transform: scale(1.05); box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3); } .game-over { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: rgba(255, 255, 255, 0.95); padding: 30px; border-radius: 20px; text-align: center; box-shadow: 0 0 30px rgba(0, 0, 0, 0.4); z-index: 20; animation: pop-in 0.3s ease-out; } @keyframes pop-in { 0% { opacity: 0; transform: translate(-50%, -50%) scale(0.8); } 100% { opacity: 1; transform: translate(-50%, -50%) scale(1); } } .game-over h2 { font-size: 32px; color: #f44336; margin-bottom: 20px; } .game-over p { font-size: 24px; margin-bottom: 20px; color: #333; } .game-over .final-score { font-size: 36px; font-weight: bold; color: #ff9800; } .restart-button { padding: 12px 30px; font-size: 18px; font-weight: bold; background: linear-gradient(to bottom, #4caf50, #2e7d32); border: none; border-radius: 30px; color: white; cursor: pointer; transition: all 0.2s; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); } .restart-button:hover { transform: scale(1.05); box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3); } .wing-flap { animation: wing-flap 0.3s infinite; } @keyframes wing-flap { 0% { transform: rotate(-25deg); } 50% { transform: rotate(20deg); } 100% { transform: rotate(-25deg); } } .ground-texture { position: absolute; bottom: 5px; left: 0; width: 100%; height: 20px; background-image: repeating-linear-gradient( to right, transparent, transparent 20px, rgba(0, 0, 0, 0.1) 20px, rgba(0, 0, 0, 0.1) 40px ); } </style> </head> <body> <div id="root"></div> <script type="text/babel"> const { useState, useEffect, useRef } = React; // Game constants const GRAVITY = 0.6; const JUMP_FORCE = -10; const PIPE_SPEED = 3; const PIPE_SPAWN_INTERVAL = 1800; const PIPE_GAP = 180; const BIRD_SIZE = 30; const GROUND_HEIGHT = 160; // Bird component const Bird = ({ position }) => { return ( <div className="bird" style={{ top: position.y + 'px', left: position.x + 'px', transform: `rotate(${position.rotation}deg)` }} > <div className="bird-body"> <div className="bird-eye"> <div className="bird-pupil"></div> </div> <div className="bird-wing wing-flap"></div> <div className="bird-beak"></div> </div> </div> ); }; // Pipe component const Pipe = ({ position, height, type }) => { return ( <div className={`pipe ${type}`} style={{ right: position + 'px' }} > <div className="pipe-top"></div> <div className="pipe-body" style={{ height: height + 'px' }}> <div style={{ flex: 1, backgroundColor: '#4da029', width: '90%' }}></div> </div> </div> ); }; // Game component const FlappyBird = () => { // Game state const [gameState, setGameState] = useState('start'); // 'start', 'playing', 'gameOver' const [birdPosition, setBirdPosition] = useState({ x: 100, y: 300, rotation: 0 }); const [birdVelocity, setBirdVelocity] = useState(0); const [pipes, setPipes] = useState([]); const [score, setScore] = useState(0); const [highScore, setHighScore] = useState(0); // Game area dimensions const gameHeight = 640; const gameWidth = 360; const gameAreaRef = useRef(null); // Game loop reference const gameLoopRef = useRef(null); // Handle jump const jump = () => { if (gameState === 'start') { startGame(); return; } if (gameState === 'playing') { setBirdVelocity(JUMP_FORCE); } else if (gameState === 'gameOver') { resetGame(); } }; // Start game const startGame = () => { setGameState('playing'); setBirdPosition({ x: 100, y: 300, rotation: 0 }); setBirdVelocity(0); setPipes([]); setScore(0); }; // Reset game const resetGame = () => { // Update high score if needed if (score > highScore) { setHighScore(score); } startGame(); }; // Game over const gameOver = () => { setGameState('gameOver'); cancelAnimationFrame(gameLoopRef.current); }; // Handle user input useEffect(() => { const handleKeyDown = (e) => { if (e.code === 'Space') { jump(); } }; const handleTouchStart = () => { jump(); }; window.addEventListener('keydown', handleKeyDown); window.addEventListener('touchstart', handleTouchStart); // Also listen for clicks on the game area to support mobile const gameArea = gameAreaRef.current; gameArea.addEventListener('click', jump); return () => { window.removeEventListener('keydown', handleKeyDown); window.removeEventListener('touchstart', handleTouchStart); gameArea.removeEventListener('click', jump); }; }, [gameState]); // Game loop useEffect(() => { if (gameState !== 'playing') return; let lastTime = 0; let pipeTimer = 0; const update = (timestamp) => { const delta = timestamp - lastTime; lastTime = timestamp; // Update bird position and velocity based on gravity setBirdPosition(prev => { const newVelocity = prev.y + (birdVelocity + GRAVITY) * (delta / 16); const rotation = Math.min(90, Math.max(-30, birdVelocity * 3)); return { ...prev, y: newVelocity, rotation }; }); setBirdVelocity(prev => prev + GRAVITY); // Update pipes pipeTimer += delta; if (pipeTimer >= PIPE_SPAWN_INTERVAL) { pipeTimer = 0; // Generate random pipe gap position const gapPosition = Math.floor(Math.random() * (gameHeight - GROUND_HEIGHT - PIPE_GAP - 100)) + 50; setPipes(prev => [ ...prev, { id: Date.now(), x: gameWidth, topHeight: gapPosition, bottomHeight: gameHeight - GROUND_HEIGHT - gapPosition - PIPE_GAP, passed: false } ]); } setPipes(prev => prev .map(pipe => { // Move pipe const newX = pipe.x - PIPE_SPEED; // Check if bird passed the pipe let passed = pipe.passed; if (!pipe.passed && newX + 70 < birdPosition.x) { setScore(prevScore => prevScore + 1); passed = true; } return { ...pipe, x: newX, passed }; }) .filter(pipe => pipe.x > -100) // Remove pipes that are off-screen ); // Request next frame gameLoopRef.current = requestAnimationFrame(update); }; gameLoopRef.current = requestAnimationFrame(update); return () => { if (gameLoopRef.current) { cancelAnimationFrame(gameLoopRef.current); } }; }, [gameState, birdVelocity]); // Collision detection useEffect(() => { if (gameState !== 'playing') return; // Ground collision if (birdPosition.y + BIRD_SIZE > gameHeight - GROUND_HEIGHT) { gameOver(); return; } // Ceiling collision if (birdPosition.y < 0) { setBirdPosition(prev => ({ ...prev, y: 0 })); setBirdVelocity(0); } // Pipe collision const birdRect = { left: birdPosition.x, right: birdPosition.x + BIRD_SIZE, top: birdPosition.y, bottom: birdPosition.y + BIRD_SIZE }; for (const pipe of pipes) { const topPipeRect = { left: gameWidth - pipe.x, right: gameWidth - pipe.x + 70, top: 0, bottom: pipe.topHeight }; const bottomPipeRect = { left: gameWidth - pipe.x, right: gameWidth - pipe.x + 70, top: gameHeight - GROUND_HEIGHT - pipe.bottomHeight, bottom: gameHeight - GROUND_HEIGHT }; // Check collision with top pipe if ( birdRect.right > topPipeRect.left && birdRect.left < topPipeRect.right && birdRect.bottom > topPipeRect.top && birdRect.top < topPipeRect.bottom ) { gameOver(); break; } // Check collision with bottom pipe if ( birdRect.right > bottomPipeRect.left && birdRect.left < bottomPipeRect.right && birdRect.bottom > bottomPipeRect.top && birdRect.top < bottomPipeRect.bottom ) { gameOver(); break; } } }, [birdPosition, pipes, gameState]); return ( <div className="game-container" ref={gameAreaRef}> <div className="game-background"> <div className="scene"> {/* Sky with clouds */} <div className="sky"> <div className="cloud"></div> <div className="cloud"></div> <div className="cloud"></div> <div className="cloud"></div> </div> {/* Pipes */} {pipes.map(pipe => ( <React.Fragment key={pipe.id}> <Pipe position={pipe.x} height={pipe.topHeight} type="upper" /> <Pipe position={pipe.x} height={pipe.bottomHeight} type="lower" /> </React.Fragment> ))} {/* Bird */} <Bird position={birdPosition} /> {/* Ground */} <div className="ground"> <div className="grass"></div> <div className="ground-texture"></div> </div> {/* Score */} {gameState !== 'start' && ( <div className="score-display"> {score} </div> )} {/* Start screen */} {gameState === 'start' && ( <div className="instructions"> <h2>FLAPPY BIRD</h2> <p>Score points without hitting pipes!</p> <div className="controls"> <div className="control-item"> <div className="key">SPACE</div> <span>Jump</span> </div> <div className="control-item"> <div className="key">Click</div> <span>or Tap</span> </div> </div> <button className="start-button" onClick={startGame}> START GAME </button> {highScore > 0 && ( <p style={{ marginTop: '20px' }}> High Score: <span className="final-score">{highScore}</span> </p> )} </div> )} {/* Game over screen */} {gameState === 'gameOver' && ( <div className="game-over"> <h2>GAME OVER</h2> <p>Your score:</p> <p className="final-score">{score}</p> {score > highScore && <p>New high score!</p>} {score <= highScore && highScore > 0 && ( <p>High score: {highScore}</p> )} <button className="restart-button" onClick={resetGame}> PLAY AGAIN </button> </div> )} </div> </div> </div> ); }; ReactDOM.createRoot(document.getElementById('root')).render(<FlappyBird />); </script> </body> </html> ``` This Flappy Bird clone includes: 1. **Physics System**: Implements gravity for the bird with proper jumping mechanics 2. **Bird Character**: Fully rendered yellow bird with wing animation 3. **Pipes**: Randomly generated pillars with varying gaps to navigate through 4. **Collision Detection**: Detects collisions with pipes, ceiling, and ground 5. **Scoring System**: Increases score when passing between pipes 6. **Game States**: Start screen, gameplay, and game over screens with score tracking 7. **High Score**: Tracks the highest score across gameplay sessions 8. **Controls**: Space bar, mouse click, or touch to make the bird jump 9. **Responsive Design**: Works on both desktop and mobile devices 10. **Visual Polish**: Detailed visuals with clouds, ground details, pipe designs, and animations To play the game: 1. Click "START GAME" on the welcome screen 2. Press SPACE, tap, or click to make the bird jump 3. Navigate through the pipes without hitting them 4. When you hit a pipe or the ground, the game ends 5. Click PLAY AGAIN to restart Enjoy the classic Flappy Bird gameplay!
Drag to resize
```html <!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://unpkg.com/react@18/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script> <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> <style> body { margin: 0; padding: 0; background: linear-gradient(to bottom, #87CEEB 0%, #98FB98 100%); font-family: Arial, sans-serif; display: flex; justify-content: center; align-items: center; min-height: 100vh; } .game-container { position: relative; width: 400px; height: 600px; background: linear-gradient(to bottom, #87CEEB 0%, #98FB98 100%); border: 2px solid #333; overflow: hidden; cursor: pointer; } .bird { position: absolute; width: 30px; height: 30px; background: #FFD700; border-radius: 50%; border: 2px solid #FFA500; transition: transform 0.1s; } .pipe { position: absolute; width: 50px; background: #228B22; border: 2px solid #006400; } .score { position: absolute; top: 20px; left: 20px; font-size: 24px; font-weight: bold; color: white; text-shadow: 2px 2px 4px rgba(0,0,0,0.5); z-index: 10; } .game-over { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0,0,0,0.8); color: white; padding: 30px; border-radius: 10px; text-align: center; z-index: 20; } .start-screen { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0,0,0,0.8); color: white; padding: 30px; border-radius: 10px; text-align: center; z-index: 20; } .button { background: #4CAF50; color: white; border: none; padding: 10px 20px; margin: 10px; border-radius: 5px; cursor: pointer; font-size: 16px; } .button:hover { background: #45a049; } </style> </head> <body> <div id="root"></div> <script type="text/babel"> const { useState, useEffect, useRef, useCallback } = React; const GAME_CONFIG = { gravity: 0.6, jumpStrength: -12, pipeWidth: 50, pipeGap: 120, pipeSpeed: 2, birdSize: 30, gameWidth: 400, gameHeight: 600 }; function FlappyBird() { const [bird, setBird] = useState({ x: 50, y: 200, velocity: 0 }); const [pipes, setPipes] = useState([]); const [score, setScore] = useState(0); const [gameState, setGameState] = useState('start'); // 'start', 'playing', 'gameOver' const [gameSpeed, setGameSpeed] = useState(1); const gameRef = useRef(null); const animationRef = useRef(null); const lastPipeRef = useRef(0); const createPipe = useCallback((x) => { const gapStart = Math.random() * (GAME_CONFIG.gameHeight - GAME_CONFIG.pipeGap - 100) + 50; return { id: Date.now(), x: x, topHeight: gapStart, bottomHeight: GAME_CONFIG.gameHeight - gapStart - GAME_CONFIG.pipeGap, passed: false }; }, []); const checkCollision = useCallback((birdPos, pipeList) => { // Check ground and ceiling collision if (birdPos.y <= 0 || birdPos.y >= GAME_CONFIG.gameHeight - GAME_CONFIG.birdSize) { return true; } // Check pipe collision for (let pipe of pipeList) { const birdLeft = birdPos.x; const birdRight = birdPos.x + GAME_CONFIG.birdSize; const birdTop = birdPos.y; const birdBottom = birdPos.y + GAME_CONFIG.birdSize; const pipeLeft = pipe.x; const pipeRight = pipe.x + GAME_CONFIG.pipeWidth; // Check if bird is horizontally aligned with pipe if (birdRight > pipeLeft && birdLeft < pipeRight) { // Check collision with top pipe if (birdTop < pipe.topHeight) { return true; } // Check collision with bottom pipe if (birdBottom > GAME_CONFIG.gameHeight - pipe.bottomHeight) { return true; } } } return false; }, []); const jump = useCallback(() => { if (gameState === 'playing') { setBird(prev => ({ ...prev, velocity: GAME_CONFIG.jumpStrength })); } }, [gameState]); const startGame = useCallback(() => { setBird({ x: 50, y: 200, velocity: 0 }); setPipes([]); setScore(0); setGameState('playing'); setGameSpeed(1); lastPipeRef.current = 0; }, []); const gameLoop = useCallback(() => { if (gameState !== 'playing') return; setBird(prevBird => { const newBird = { ...prevBird, velocity: prevBird.velocity + GAME_CONFIG.gravity, y: prevBird.y + prevBird.velocity + GAME_CONFIG.gravity }; return newBird; }); setPipes(prevPipes => { let newPipes = prevPipes.map(pipe => ({ ...pipe, x: pipe.x - GAME_CONFIG.pipeSpeed * gameSpeed })).filter(pipe => pipe.x > -GAME_CONFIG.pipeWidth); // Add new pipes if (newPipes.length === 0 || newPipes[newPipes.length - 1].x < GAME_CONFIG.gameWidth - 200) { newPipes.push(createPipe(GAME_CONFIG.gameWidth)); } return newPipes; }); // Check for scoring setPipes(prevPipes => { const newPipes = [...prevPipes]; newPipes.forEach(pipe => { if (!pipe.passed && pipe.x + GAME_CONFIG.pipeWidth < bird.x) { pipe.passed = true; setScore(prev => prev + 1); // Increase game speed slightly every 5 points if ((score + 1) % 5 === 0) { setGameSpeed(prev => Math.min(prev + 0.2, 3)); } } }); return newPipes; }); // Check collisions if (checkCollision(bird, pipes)) { setGameState('gameOver'); } }, [gameState, bird, pipes, score, gameSpeed, checkCollision, createPipe]); // Game loop effect useEffect(() => { if (gameState === 'playing') { animationRef.current = requestAnimationFrame(gameLoop); return () => { if (animationRef.current) { cancelAnimationFrame(animationRef.current); } }; } }, [gameLoop, gameState]); // Handle click and keyboard events useEffect(() => { const handleInteraction = (e) => { if (gameState === 'start') { startGame(); } else if (gameState === 'playing') { jump(); } else if (gameState === 'gameOver') { startGame(); } }; const handleKeyPress = (e) => { if (e.code === 'Space' || e.key === ' ') { e.preventDefault(); handleInteraction(); } }; window.addEventListener('keydown', handleKeyPress); return () => { window.removeEventListener('keydown', handleKeyPress); }; }, [gameState, jump, startGame]); return ( <div className="game-container" ref={gameRef} onClick={() => { if (gameState === 'start') { startGame(); } else if (gameState === 'playing') { jump(); } else if (gameState === 'gameOver') { startGame(); } }} > <div className="score">Score: {score}</div> {/* Bird */} <div className="bird" style={{ left: bird.x, top: bird.y, transform: `rotate(${Math.min(Math.max(bird.velocity * 3, -30), 30)}deg)` }} /> {/* Pipes */} {pipes.map(pipe => ( <React.Fragment key={pipe.id}> {/* Top pipe */} <div className="pipe" style={{ left: pipe.x, top: 0, height: pipe.topHeight }} /> {/* Bottom pipe */} <div className="pipe" style={{ left: pipe.x, bottom: 0, height: pipe.bottomHeight }} /> </React.Fragment> ))} {/* Start Screen */} {gameState === 'start' && ( <div className="start-screen"> <h1>Flappy Bird</h1> <p>Click or press SPACE to start</p> <p>Click or press SPACE to jump</p> <button className="button" onClick={startGame}> Start Game </button> </div> )} {/* Game Over Screen */} {gameState === 'gameOver' && ( <div className="game-over"> <h2>Game Over!</h2> <p>Final Score: {score}</p> <p>Click or press SPACE to restart</p> <button className="button" onClick={startGame}> Play Again </button> </div> )} </div> ); } ReactDOM.render(<FlappyBird />, document.getElementById('root')); </script> </body> </html> ``` This Flappy Bird clone includes all the requested features: **Core Features:** - **Gravity**: The bird continuously falls due to gravity - **Jumping**: Click or press SPACE to make the bird jump - **Pipes**: Green obstacles with gaps that move from right to left - **Scoring**: Points awarded for passing through pipes - **Collision Detection**: Game ends when hitting pipes, ground, or ceiling **Additional Features:** - **Progressive Difficulty**: Game speed increases every 5 points - **Visual Effects**: Bird rotates based on velocity - **Responsive Controls**: Works with both mouse clicks and keyboard - **Game States**: Start screen, playing, and game over screens - **Score Display**: Real-time score tracking **Game Controls:** - Click anywhere or press SPACE to start the game - Click or press SPACE to make the bird jump - Click or press SPACE to restart after game over The game uses React hooks for state management and includes a smooth game loop with collision detection. The bird physics feel authentic with proper gravity and jump mechanics that mirror the original Flappy Bird experience.
Drag to resize