使用双缓冲技术可以避免画面闪烁问题。具体做法是创建一个与窗口大小相同的图像缓冲区,将所有的绘制操作先绘制到缓冲区中,然后再一次性将缓冲区中的内容绘制到窗口上。

下面是修改后的代码:

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;

public class CatchingGame extends JFrame implements KeyListener {
    private static final int FRAME_WIDTH = 800;
    private static final int FRAME_HEIGHT = 600;
    private static final int PLAYER_WIDTH = 50;
    private static final int PLAYER_HEIGHT = 50;
    private static final int ITEM_WIDTH = 30;
    private static final int ITEM_HEIGHT = 30;
    private static final int ITEM_SPEED = 5;
    private static final int GAME_DURATION = 60;

    private int playerX;
    private int playerY;
    private int score;
    private int timeLeft;
    private boolean isGameRunning;
    private List<Point> items;

    private Image iBuffer;
    private Graphics gBuffer;

    public CatchingGame() {
        setTitle('接元宝游戏');
        setSize(FRAME_WIDTH, FRAME_HEIGHT);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(1200, 700);
        JPanel panel=new JPanel();
        panel.setLayout(null);
        JLabel background=new JLabel();
        background.setIcon(new ImageIcon('D:\EatMooncake\src\bg.jpg'));
        background.setBounds(0,0,getWidth(),getHeight());
        panel.add(background);
        getContentPane().add(panel);
        setResizable(false);
        addKeyListener(this);

        playerX = (FRAME_WIDTH - PLAYER_WIDTH) / 2;
        playerY = FRAME_HEIGHT - PLAYER_HEIGHT;
        score = 0;
        timeLeft = GAME_DURATION;
        isGameRunning = true;
        items = new ArrayList<>();

        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                if (isGameRunning) {
                    generateItem();
                    timeLeft--;
                    if (timeLeft <= 0) {
                        endGame();
                    }
                }
            }
        }, 1000, 1000);

        Timer timer2 = new Timer();
        timer2.schedule(new TimerTask() {
            @Override
            public void run() {
                if (isGameRunning) {
                    moveItems();
                    checkCollision();
                    repaint();
                }
            }
        }, 0, 20);

        setVisible(true);
        Sound player = new Sound('D:\EatMooncake\src\但愿人长久.wav');
        player.start(true);
    }

    private synchronized void generateItem() {
        Random random = new Random();
        int itemX = random.nextInt(FRAME_WIDTH - ITEM_WIDTH);
        int itemY = 0;
        items.add(new Point(itemX, itemY));
    }

    private synchronized void moveItems() {
        for (Point item : items) {
            item.y += ITEM_SPEED;
        }
        items.removeIf(item -> item.y >= FRAME_HEIGHT);
    }

    private synchronized void checkCollision() {
        Rectangle playerRect = new Rectangle(playerX, playerY, PLAYER_WIDTH, PLAYER_HEIGHT);
        for (Point item : items) {
            Rectangle itemRect = new Rectangle(item.x, item.y, ITEM_WIDTH, ITEM_HEIGHT);
            if (playerRect.intersects(itemRect)) {
                score++;
                items.remove(item);
                break;
            }
        }
    }

    private void endGame() {
        isGameRunning = false;
        JOptionPane.showMessageDialog(this, '游戏结束,您的得分为:' + score);
        dispose();
    }

    @Override
    public void paint(Graphics g) {
        if (iBuffer == null) {
            iBuffer = createImage(FRAME_WIDTH, FRAME_HEIGHT);
            gBuffer = iBuffer.getGraphics();
        }

        gBuffer.setColor(Color.GREEN);
        gBuffer.fillRect(playerX, playerY, PLAYER_WIDTH, PLAYER_HEIGHT);
        gBuffer.setColor(Color.YELLOW);
        for (Point item : items) {
            gBuffer.fillOval(item.x, item.y, ITEM_WIDTH, ITEM_HEIGHT);
        }

        gBuffer.setColor(Color.WHITE);
        gBuffer.drawString('得分:' + score, 10, 60);
        gBuffer.drawString('剩余时间:' + timeLeft + '秒', 10, 40);

        g.drawImage(iBuffer, 0, 0, this);
    }

    @Override
    public void keyTyped(KeyEvent e) {}

    @Override
    public void keyPressed(KeyEvent e) {
        if (isGameRunning) {
            int keyCode = e.getKeyCode();
            switch (keyCode) {
                case KeyEvent.VK_UP:
                    playerY -= 10;
                    break;
                case KeyEvent.VK_DOWN:
                    playerY += 10;
                    break;
                case KeyEvent.VK_LEFT:
                    playerX -= 10;
                    break;
                case KeyEvent.VK_RIGHT:
                    playerX += 10;
                    break;
            }
            playerX = Math.max(0, Math.min(playerX, FRAME_WIDTH - PLAYER_WIDTH));
            playerY = Math.max(0, Math.min(playerY, FRAME_HEIGHT - PLAYER_HEIGHT));
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {}

    public static void main(String[] args) {
        new CatchingGame();
    }
}

在原有的paint方法中,先创建一个与窗口大小相同的图像缓冲区iBuffer,然后将所有的绘制操作先绘制到缓冲区gBuffer中,最后再一次性将缓冲区中的内容绘制到窗口上。这样可以避免绘制过程中的闪烁问题。

Java Swing 游戏闪烁问题解决方案:双缓冲技术

原文地址: https://www.cveoy.top/t/topic/pBw 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录