You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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 paint method has infinite loops and Thread.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 randY and number aren'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 Timer instead of infinite loops and Thread.sleep—this keeps the UI responsive and follows Swing's threading rules.
  • Fixed Pipe Spacing: Pipes spawn with PIPE_SPACING pixels 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, and GRAVITY to adjust the game feel.
  • If you want to add scoring, just increment a counter every time a pipe passes the bird.

内容的提问来源于stack exchange,提问作者Vish

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 09:17:35