Java Swing开发Flappy Bird:随机管道生成与碰撞检测求助
Fixing Your Flappy Bird Swing Implementation: Random Height Pipes & Collision Detection
Hey there! Let's get your Flappy Bird clone up and running properly. Your current code has a few key issues (like blocking the Swing UI thread with infinite loops and sleep calls) that are preventing it from working, plus missing logic for pipe spacing and collision detection. Let's fix this step by step.
First, Let's Break Down the Core Problems in Your Code
- Blocking the UI Thread: Your
paintmethod has infinite loops andThread.sleep, which freezes Swing's event dispatch thread—this is why your game isn't rendering properly. - Unorganized Pipe Logic: You're trying to create pipes but not storing them in a manageable list, and there's no logic for fixed horizontal spacing between pipes.
- No Input Handling: Your bird just falls forever—no way to make it jump.
- Missing Collision Detection: You have a bird rectangle and pipe rectangles, but no code to check if they intersect.
- Uninitialized Variables: Many variables like
randYandnumberaren't set properly, leading to unpredictable behavior.
Fixed & Working Code Implementation
Here's a revised version of your code that addresses all these issues, with random-height pipes, fixed spacing, collision detection, and basic bird control:
import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.ArrayList; import java.util.Random; public class FlappyBirdGame extends JFrame implements ActionListener, KeyListener, MouseListener { // Game constants private static final int SCREEN_WIDTH = 404; private static final int SCREEN_HEIGHT = 600; private static final int PIPE_WIDTH = 50; private static final int PIPE_SPACING = 200; // Fixed horizontal spacing between pipes private static final int PIPE_GAP = 150; // Gap between top and bottom pipes private static final int BIRD_SIZE = 20; private static final int GRAVITY = 1; private static final int JUMP_STRENGTH = -15; // Game objects private Rectangle bird; private ArrayList<Pipe> pipes; private Timer gameTimer; private Random random; private int birdYVelocity; private boolean gameRunning; // Background (use your image paths here) private ImageIcon background; public FlappyBirdGame() { // Initialize frame setTitle("Flappy Bird"); setSize(SCREEN_WIDTH, SCREEN_HEIGHT); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(null); setResizable(false); setLocationRelativeTo(null); // Initialize game objects random = new Random(); background = new ImageIcon("Test1.jpg"); // Replace with your actual background path bird = new Rectangle(SCREEN_WIDTH / 4, SCREEN_HEIGHT / 2, BIRD_SIZE, BIRD_SIZE); birdYVelocity = 0; gameRunning = true; // Initialize pipes pipes = new ArrayList<>(); spawnInitialPipes(); // Setup timer for game loop (60 FPS approx) gameTimer = new Timer(16, this); gameTimer.start(); // Add input listeners addKeyListener(this); addMouseListener(this); setFocusable(true); setVisible(true); } // Spawn initial set of pipes with fixed spacing private void spawnInitialPipes() { for (int i = 1; i <= 3; i++) { int xPos = SCREEN_WIDTH + (i * PIPE_SPACING); spawnPipe(xPos); } } // Spawn a single pipe pair (top and bottom) with random height private void spawnPipe(int xPos) { int bottomPipeHeight = random.nextInt(SCREEN_HEIGHT - PIPE_GAP - 100) + 50; // Random height between 50 and (screen height - gap - 50) int topPipeHeight = SCREEN_HEIGHT - bottomPipeHeight - PIPE_GAP; Rectangle topPipe = new Rectangle(xPos, 0, PIPE_WIDTH, topPipeHeight); Rectangle bottomPipe = new Rectangle(xPos, SCREEN_HEIGHT - bottomPipeHeight, PIPE_WIDTH, bottomPipeHeight); pipes.add(new Pipe(topPipe, bottomPipe)); } @Override public void paint(Graphics g) { super.paint(g); if (gameRunning) { // Draw background background.paintIcon(this, g, 0, 0); // Draw bird g.setColor(Color.RED); g.fillRect(bird.x, bird.y, bird.width, bird.height); // Draw pipes g.setColor(Color.GREEN); for (Pipe pipe : pipes) { g.fillRect(pipe.top.x, pipe.top.y, pipe.top.width, pipe.top.height); g.fillRect(pipe.bottom.x, pipe.bottom.y, pipe.bottom.width, pipe.bottom.height); } } else { // Game over screen g.setColor(Color.BLACK); g.setFont(new Font("Arial", Font.BOLD, 30)); g.drawString("GAME OVER", SCREEN_WIDTH / 2 - 80, SCREEN_HEIGHT / 2); } } @Override public void actionPerformed(ActionEvent e) { if (gameRunning) { // Update bird physics birdYVelocity += GRAVITY; bird.y += birdYVelocity; // Keep bird within screen bounds (optional, or trigger game over if it hits top/bottom) if (bird.y < 0 || bird.y + bird.height > SCREEN_HEIGHT) { gameRunning = false; } // Update pipe positions and spawn new ones for (int i = 0; i < pipes.size(); i++) { Pipe pipe = pipes.get(i); pipe.top.x -= 2; pipe.bottom.x -= 2; // Remove pipes that go off-screen if (pipe.top.x + PIPE_WIDTH < 0) { pipes.remove(i); i--; // Spawn new pipe at the end spawnPipe(SCREEN_WIDTH); } // Check collision with bird if (bird.intersects(pipe.top) || bird.intersects(pipe.bottom)) { gameRunning = false; } } // Trigger repaint repaint(); } else { gameTimer.stop(); } } // Bird jump on key press (space bar) @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_SPACE && gameRunning) { birdYVelocity = JUMP_STRENGTH; } } // Bird jump on mouse click @Override public void mouseClicked(MouseEvent e) { if (gameRunning) { birdYVelocity = JUMP_STRENGTH; } } // Helper class to hold top and bottom pipe pairs private static class Pipe { Rectangle top; Rectangle bottom; Pipe(Rectangle top, Rectangle bottom) { this.top = top; this.bottom = bottom; } } // Unused listener methods (required by interfaces) @Override public void keyReleased(KeyEvent e) {} @Override public void keyTyped(KeyEvent e) {} @Override public void mousePressed(MouseEvent e) {} @Override public void mouseReleased(MouseEvent e) {} @Override public void mouseEntered(MouseEvent e) {} @Override public void mouseExited(MouseEvent e) {} public static void main(String[] args) { new FlappyBirdGame(); } }
Key Improvements Explained
- Swing-Friendly Game Loop: Used a
Timerinstead of infinite loops andThread.sleep—this keeps the UI responsive and follows Swing's threading rules. - Fixed Pipe Spacing: Pipes spawn with
PIPE_SPACINGpixels between them, so they're evenly distributed. - Random Pipe Heights: The bottom pipe's height is randomized, and the top pipe's height is calculated to maintain a fixed gap (
PIPE_GAP) between the two. - Collision Detection: Used
Rectangle.intersects()to check if the bird hits any pipe, or if it flies outside the screen bounds. - Bird Control: Added mouse click and space bar support to make the bird jump against gravity.
- Cleaner Object Management: Used an
ArrayList<Pipe>to track all pipes, and removed off-screen pipes to save memory.
Quick Notes for You
- Make sure your background image path is correct (
Test1.jpg)—if the image doesn't load, the background will just be the default frame color. - You can tweak constants like
PIPE_SPACING,PIPE_GAP,JUMP_STRENGTH, andGRAVITYto adjust the game feel. - If you want to add scoring, just increment a counter every time a pipe passes the bird.
内容的提问来源于stack exchange,提问作者Vish
相关产品推荐
相关产品推荐

