【问题标题】:Flickering images in JPanelJPanel中闪烁的图像
【发布时间】:2011-10-11 23:32:07
【问题描述】:

我创建了一个将图像绘制到 JPanel 的游戏。这与 JTimer 一起运行,它每 14 毫秒执行一次以移动背景、移动障碍物并移动玩家(直升机)。我偶尔会出现背景和障碍物的闪烁,并且它运行得不是特别顺畅。 有没有办法解决这个问题?我需要在不同的类中使用线程和动画吗? 我也使用了双缓冲,但后来发现 JPanel 无论如何都是双缓冲的,所以我将其删除。

这是 Panel 类的代码。

class ImagePanel extends JPanel implements KeyListener, MouseListener,
        MouseMotionListener {

    /**
     * 
     */
    private static final long serialVersionUID = -6096603231469523786L;
    Plane o1 = new Plane();
    Tank o2 = new Tank();

    ArrayList<Plane> planeArray;
    ArrayList<Tank> tankArray;
    ArrayList<Missile> missileArray;

    int bgx;
    int bgxTwo;
    int time;
    int y;
    int score;
    int obsNum;
    int obsNum2;
    int planeSpeed;
    int missileSpeed;
    int count;

    boolean holding;
    boolean gameOver;
    boolean startscreen;
    boolean shooting;
    boolean obsOne = false;
    boolean obsTwo = false;

    Color colorone = Color.WHITE;
    Color colortwo = Color.WHITE;
    Color restartColor = Color.white;
    Color menuColor = Color.white;
    Color bulletColor = Color.white;

    Font f1;
    Font f2;
    Font f3;

    String version = "Get To The Chopper Beta 1.2";

    int level = 1;

    java.util.Timer movementtimer;
    TimerTask task;

    java.util.Timer obstacletimer;
    TimerTask taskTwo;

    private Image image;
    private Image imageTwo;
    private Image chopper;
    private Image chopper2;
    private Image tank;
    private Image plane;
    private Image missile;
    private Image explosion;

    BufferedImage bufferedImage;
    Graphics buffer;

    java.net.URL bgurl;
    java.net.URL curl;
    java.net.URL c2url;
    java.net.URL turl;
    java.net.URL purl;
    java.net.URL murl;
    java.net.URL eurl;

    // ///////// CONSTRUCTOR SETS NON-CHANGABLE VARIABLES ////////////////

    public ImagePanel() {

        setFocusable(true);
        addKeyListener(this);
        addMouseListener(this);
        addMouseMotionListener(this);

        f1 = new Font("Helvetica", Font.BOLD, 10);
        f2 = new Font("Helvetica", Font.PLAIN, 30);
        f3 = new Font("Helvetica", Font.PLAIN, 25);

        startscreen = true;

        getImages();

        planeArray = new ArrayList<Plane>();
        tankArray = new ArrayList<Tank>();
        missileArray = new ArrayList<Missile>();

        repaint();
    }

    public void getImages() {

        bgurl = getClass().getResource("images/background.png");
        switch (level) {
        case (1):
            bgurl = getClass().getResource("images/grassbackground.png");
            bulletColor = Color.black;
            break;
        case (2):
            bgurl = getClass().getResource("images/snowbackground.png");
            bulletColor = Color.red;
            break;
        }
        curl = getClass().getResource("images/heli.png");
        c2url = getClass().getResource("images/heli2.png");
        turl = getClass().getResource("images/tank.png");
        purl = getClass().getResource("images/plane.png");
        murl = getClass().getResource("images/missile.png");
        eurl = getClass().getResource("images/explosion.png");

        try {

            image = Toolkit.getDefaultToolkit().getImage(bgurl);
            imageTwo = Toolkit.getDefaultToolkit().getImage(bgurl);
            chopper = Toolkit.getDefaultToolkit().getImage(curl);
            chopper2 = Toolkit.getDefaultToolkit().getImage(c2url);
            tank = Toolkit.getDefaultToolkit().getImage(turl);
            plane = Toolkit.getDefaultToolkit().getImage(purl);
            missile = Toolkit.getDefaultToolkit().getImage(murl);
            explosion = Toolkit.getDefaultToolkit().getImage(eurl);

        } catch (Exception ex) {
            System.out.println("File Not Found");
        }

    }

    // //////// SETS VARIABLES WHEN GAME IS STARTED OR RESTARTED
    // //////////////////

    public void startGame() {
        bgx = 0;
        bgxTwo = 800;
        y = 50;
        score = 0;
        holding = false;
        gameOver = false;
        time = (int) (Math.random() * 500 + 500);
        obsNum2 = 0;
        shooting = false;

        count = 1;

        planeSpeed = -6;
        missileSpeed = -7;

        planeArray.clear();
        tankArray.clear();
        missileArray.clear();

        colorone = Color.WHITE;
        colortwo = Color.WHITE;
        restartColor = Color.white;
        menuColor = Color.white;

        movementtimer = new java.util.Timer();
        task = new TimerTask() {
            public void run() {
                moveImage();repaint();
            }
        };
        movementtimer.schedule(task, 0, 13);

        obstacletimer = new java.util.Timer();
        taskTwo = new TimerTask() {
            public void run() {
                generateObstacle();
            }
        };
        obstacletimer.schedule(taskTwo, 2000, time);

    }

    // ////////// COLLISION DETECTION //////////////////

    public void moveImage() {

        moveBackground();

        addToScore();

        if (holding) {
            y += -3;
        } else {
            y += 3;
        }

        for (int i = 0; i < planeArray.size(); i++) {
            Plane plane = planeArray.get(i);
            if (y + 30 >= plane.yPos && y + 30 <= plane.yPos + 30
                    && plane.xPos >= 200 && plane.xPos <= 250
                    || y >= plane.yPos && y <= plane.yPos + 30
                    && plane.xPos >= 200 && plane.xPos + 50 <= 250) {
                gameOver = true;
                System.out.println("Crash with plane!");
            }
        }

        for (int i = 0; i < tankArray.size(); i++) {
            Tank tank = tankArray.get(i);
            if (y + 30 >= tank.yPos && y + 30 <= tank.yPos + 30
                    && tank.xPos >= 200 && tank.xPos <= 250
                    || y >= tank.yPos && y <= tank.yPos + 30
                    && tank.xPos >= 200 && tank.xPos + 50 <= 250) {
                gameOver = true;
                System.out.println("Crash with a tank");
            }
            if (shooting) {
                if (y + 30 >= tank.bulletYPos && y <= tank.bulletYPos
                        && tank.bulletXPos >= 200 && tank.bulletXPos <= 250) {
                    gameOver = true;
                    System.out.println("Shot down");

                }
            }
        }

        for (int i = 0; i < missileArray.size(); i++) {
            Missile missile = missileArray.get(i);
            if (y + 30 >= missile.yPos && y + 30 <= missile.yPos + 30
                    && missile.xPos >= 200 && missile.xPos <= 250
                    || y >= missile.yPos && y <= missile.yPos + 30
                    && missile.xPos >= 200 && missile.xPos + 50 <= 250) {
                gameOver = true;
                System.out.println("Crash with a missile");
            }
        }

        if (y >= 300) {
            gameOver = true;
        } else if (y <= -15) {
            gameOver = true;
        }


    }

    // //////// MOVES BACKGROUND //////////////

    public void moveBackground() {

        bgx += -5;

        if (bgx == -800) {
            bgx = 800;
        }

        bgxTwo += -5;

        if (bgxTwo == -800) {
            bgxTwo = 800;
        }

        for (int i = 0; i < planeArray.size(); i++) {

            Plane obs = planeArray.get(i);
            obs.xPos = obs.xPos - 6;
        }

        for (int i = 0; i < tankArray.size(); i++) {

            Tank obs = tankArray.get(i);

            obs.xPos = obs.xPos - 5;
            if (shooting) {
                obs.bulletYPos = obs.bulletYPos - 2;
                obs.bulletXPos = obs.bulletXPos - obs.angle;
            }
        }

        for (int i = 0; i < missileArray.size(); i++) {

            Missile obs = missileArray.get(i);
            obs.xPos = obs.xPos - 7;
        }

    }

    public void addToScore() {

        score += 5;

        increaseDiff();

    }

    public void increaseDiff() {

        if (score == 10000) {
            shooting = true;
            for (int i = 0; i < tankArray.size(); i++) {
                Tank tank = tankArray.get(i);
                tank.bulletXPos = tank.xPos;
            }
        }
        if (score > 5000 && score < 9999) {
            time = (int) (Math.random() * 400 + 250);
            planeSpeed = -7;
            missileSpeed = -8;

        } else if (score > 10000 && score < 14999) {
            time = (int) (Math.random() * 400 + 150);
            planeSpeed = -8;
            missileSpeed = -10;

        } else if (score > 15000 && score < 19999) {
            time = (int) (Math.random() * 400 + 150);
            planeSpeed = -9;
            missileSpeed = -11;
            obsNum2 = (int) (Math.random() * 2 + 1);
        } else if (score > 20000 && score < 29999) {
            time = (int) (Math.random() * 300 + 100);
            planeSpeed = -10;
            missileSpeed = -13;
            obsNum2 = (int) (Math.random() * 2 + 1);
        } else if (score > 30000) {
            time = (int) (Math.random() * 300 + 100);
            planeSpeed = -12;
            missileSpeed = -15;
            obsNum2 = (int) (Math.random() * 2 + 1);
        }
    }

    public void generateObstacle() {

        obsNum = (int) (Math.random() * 5 + 1);

        if (obsNum == 1 || obsNum == 4 || obsNum2 == 1) {
            planeArray.add(new Plane());
        }

        if (obsNum == 2) {
            tankArray.add(new Tank());
        }

        if (obsNum == 3 || obsNum == 5 || obsNum2 == 2) {
            missileArray.add(new Missile());
        }

    }


    public void update(Graphics g) {
        paint(g);
    }

    // /////////////// MAIN PAINT METHOD //////////////////////

    @Override
    public void paint(Graphics g) {

        g.clearRect(0,0,800,400);

        if (!startscreen) {

            g.drawImage(image, bgx, 0, null);
            g.drawImage(imageTwo, bgxTwo, 0, null);

            for (int i = 0; i < planeArray.size(); i++) {
                Plane obs = planeArray.get(i);

                if (obs.xPos <= -50) {
                    planeArray.remove(i);
                } else {

                    g.drawImage(plane, obs.xPos, obs.yPos, null);
                }
            }

            for (int i = 0; i < tankArray.size(); i++) {
                Tank obs = tankArray.get(i);

                if (obs.xPos <= -50) {
                    tankArray.remove(i);
                } else {

                    g.drawImage(tank, obs.xPos, obs.yPos, null);
                    if (shooting) {
                        g.setColor(bulletColor);
                        g.fillOval(obs.bulletXPos, obs.bulletYPos, 5,
                                5);
                    }
                }
            }

            for (int i = 0; i < missileArray.size(); i++) {
                Missile obs = missileArray.get(i);

                if (obs.xPos <= -50) {
                    missileArray.remove(i);
                } else {

                    g.drawImage(missile, obs.xPos, obs.yPos, null);
                }
            }

            if (gameOver) {
                movementtimer.cancel();
                obstacletimer.cancel();
                g.setColor(Color.WHITE);
                g.setFont(f2);
                g.drawString("GAME OVER", 250, 150);
                g.setFont(f3);
                g.drawString("Your score: " + score, 250, 200);
                g.setColor(restartColor);
                g.drawString("Click to restart!", 250, 250);
                g.setColor(menuColor);
                g.drawString("Back to menu", 250, 300);
                g.drawImage(explosion, 200, y, this);
            }

        } else if (startscreen) {

            g.drawImage(image, 0, 0, this);
            g.drawImage(imageTwo, 0, 0, this);
            g.drawImage(chopper, 200, 50, this);
            g.setColor(Color.WHITE);
            g.setFont(f2);
            g.drawString("Choose a level:", 200, 150);
            g.setFont(f3);
            g.setColor(colorone);
            g.drawString("Grass Level!", 250, 200);
            g.setColor(colortwo);
            g.drawString("Snow Level!", 250, 250);

        }

        if (count <= 9 && !gameOver && !startscreen) {
            g.drawImage(chopper, 200, y, this);
            count++;
        } else if (count >= 10 && count < 20 && !gameOver && !startscreen) {
            g.drawImage(chopper2, 200, y, this);
            count++;
        } else if (count == 20 && !gameOver && !startscreen) {
            count = 1;
            g.drawImage(chopper, 200, y, this);
        }

        g.setFont(f1);
        g.setColor(Color.WHITE);
        g.drawString(version, 10, 10);
        g.drawString("Score: " + score + "", 700, 10);
    }



    // ///////////////////OVERRIDE KEYPRESSED EVENTS//////////////////////

    @Override
    public void keyPressed(KeyEvent e) {
        // TODO Auto-generated method stub

        if (e.getKeyCode() == 32) {
            holding = true;
            repaint();
        }

    }

    @Override
    public void keyReleased(KeyEvent e) {
        // TODO Auto-generated method stub

        if (e.getKeyCode() == 32) {
            holding = false;
            repaint();
        }

    }

    @Override
    public void keyTyped(KeyEvent e) {
        // TODO Auto-generated method stub

    }

    public void mousePressed(MouseEvent e) {

        if (e.getX() >= 250 && e.getX() <= 400 && e.getY() >= 180
                && e.getY() <= 200 && startscreen) {
            startscreen = false;
            level = 1;
            getImages();
            startGame();
        }

        if (e.getX() >= 250 && e.getX() <= 400 && e.getY() >= 230
                && e.getY() <= 250 && startscreen) {
            startscreen = false;
            level = 2;
            getImages();
            startGame();
        }

        if (e.getX() >= 250 && e.getX() <= 420 && e.getY() >= 230
                && e.getY() <= 250 && gameOver) {
            startGame();
        }

        if (e.getX() >= 250 && e.getX() <= 420 && e.getY() >= 280
                && e.getY() <= 320 && gameOver) {
            gameOver = false;
            startscreen = true;
            repaint();
        }

    }

    @Override
    public void mouseClicked(MouseEvent arg0) {
        // TODO Auto-generated method stub

    }

    @Override
    public void mouseEntered(MouseEvent e) {
        // TODO Auto-generated method stub

    }

    public void mouseMoved(MouseEvent e) {
        if (e.getX() >= 250 && e.getX() <= 400 && e.getY() >= 180
                && e.getY() <= 200 && startscreen) {
            colorone = Color.RED;
            repaint();
        } else if (e.getX() >= 250 && e.getX() <= 400 && e.getY() >= 230
                && e.getY() <= 250 && startscreen) {
            colortwo = Color.RED;
            repaint();
        } else if (startscreen) {
            colorone = Color.white;
            colortwo = Color.white;
            repaint();
        }

        if (e.getX() >= 250 && e.getX() <= 420 && e.getY() >= 230
                && e.getY() <= 250 && gameOver) {
            restartColor = Color.red;
            repaint();
        } else if (e.getX() >= 250 && e.getX() <= 420 && e.getY() >= 280
                && e.getY() <= 320 && gameOver) {
            menuColor = Color.red;
            repaint();
        } else if (gameOver) {
            restartColor = Color.white;
            menuColor = Color.white;
            repaint();
        }
    }

    @Override
    public void mouseExited(MouseEvent arg0) {
        // TODO Auto-generated method stub

    }

    @Override
    public void mouseReleased(MouseEvent arg0) {
        // TODO Auto-generated method stub

    }

    @Override
    public void mouseDragged(MouseEvent arg0) {
        // TODO Auto-generated method stub

    }
}

对于所有代码,您可能会说,我很抱歉,我是 Java 和一般编程的新手(去年从大学开始),但我希望以此取得进步并以此为职业。

谢谢 汤姆

【问题讨论】:

    标签: java swing flicker graphics2d


    【解决方案1】:

    不要使用 TimerTask。

    您应该使用 Swing Timer。然后对 GUI 的所有更新都将在 Event Dispatch Thread 上完成。

    【讨论】:

    • 我已经实现了一个 Swing Timer,但我只发现了它与 ActionListener 一起使用的示例。当我使用这个示例时,它会在鼠标移动时重新绘制。我不希望这种情况发生,因为即使在游戏结束屏幕上它也会继续。
    • 没关系,已修复!感谢您的信息!这是我移动一些代码的方式的问题。
    【解决方案2】:

    您正试图在 Swing 程序中制作 AWT 图形——不要这样做。取而代之的是,在 Swing 图形程序中没有更新的地方,您不应该重写paint 方法,而应该重写 JPanel 的 paintComponent 方法。这样您就可以利用 Swing 的双缓冲。

    这是一个 SSCCE,它显示了 AWT 与 Swing 动画,但不幸的是,我没有看到两者之间的差异,正如我所希望的那样。

    import java.awt.*;
    import java.awt.event.*;
    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import java.net.MalformedURLException;
    import java.net.URL;
    import javax.imageio.ImageIO;
    import javax.swing.*;
    
    @SuppressWarnings("serial")
    public class PaintVsPaintComponent extends JPanel {
       public static final String DUKE_WAVE = "http://duke.kenai.com/iconSized/duke4.gif";
       private static final int PREF_WIDTH = 300;
       private static final int PREF_HEIGHT = 250;
       private static final int TIMER_DELAY = 20;
       private static final int DELTA_X = 1;
       private static final int DELTA_Y = DELTA_X;
       private boolean awtDrawing;
       private BufferedImage image;
       private int x = 0;
       private int y = 0;
    
       public PaintVsPaintComponent(boolean awtDrawing, BufferedImage image) {
          this.awtDrawing = awtDrawing;
          this.image = image;
    
          new Timer(TIMER_DELAY, new ActionListener() {
             public void actionPerformed(ActionEvent ae) {
                timerActionPerformed(ae);
             }
          }).start();
       }
    
       private void timerActionPerformed(ActionEvent ae) {
          x += DELTA_X;
          y += DELTA_Y;
    
          if (x >= getWidth()) {
             x = 0;
          }
          if (y >= getHeight()) {
             y = 0;
          }
    
          repaint();
       }
    
       @Override
       public void paint(Graphics g) {
          super.paint(g);
          if (awtDrawing) {
             drawImage(g);
          }
       }
    
       @Override
       protected void paintComponent(Graphics g) {
          super.paintComponent(g);
          if (!awtDrawing) {
             drawImage(g);
          }
       }
    
       private void drawImage(Graphics g) {
          g.drawImage(image, x, y, null);
       }
    
       @Override
       public void update(Graphics g) {
          if (awtDrawing) {
             paint(g);
          } else {
             super.update(g);
          }
       }
    
       @Override
       public Dimension getPreferredSize() {
          return new Dimension(PREF_WIDTH, PREF_HEIGHT);
       }
    
       private static void createAndShowGui() {
          BufferedImage dukeWaveImage = null;
    
          URL dukeWaveUrl;
          try {
             dukeWaveUrl = new URL(DUKE_WAVE);
             dukeWaveImage = ImageIO.read(dukeWaveUrl);
    
    
             PaintVsPaintComponent awtPanel = new PaintVsPaintComponent(true, dukeWaveImage);
             PaintVsPaintComponent swingPanel = new PaintVsPaintComponent(false, dukeWaveImage);
             awtPanel.setBorder(BorderFactory.createTitledBorder("AWT Panel"));
             swingPanel.setBorder(BorderFactory.createTitledBorder("Swing Panel"));
    
             JPanel gridPanel = new JPanel(new GridLayout(1, 0));
             gridPanel.add(awtPanel);
             gridPanel.add(swingPanel);
    
             JFrame frame = new JFrame("PaintVsPaintComponent");
             frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
             frame.getContentPane().add(gridPanel);
             frame.pack();
             frame.setLocationByPlatform(true);
             frame.setVisible(true);
          } catch (MalformedURLException e) {
             e.printStackTrace();
          } catch (IOException e) {
             e.printStackTrace();
          }
    
       }
    
       public static void main(String[] args) {
          SwingUtilities.invokeLater(new Runnable() {
             public void run() {
                createAndShowGui();
             }
          });
       }
    
    }
    

    【讨论】:

    • +1,请参阅Painting in AWT and Swing 了解差异。
    • 感谢您的建议!我已经通过将paint() 方法更改为paintComponent() 来更改我的代码,并且我已经删除了update() 方法。但是,它仍然有点摇晃(图像会在短时间内丢失,背景 + 图像在移动时有点摇晃)。有没有办法来解决这个问题?谢谢!
    • 鉴于您的代码大小以及我们无法编译或运行您的代码这一事实,很难说。我建议你缩小它,创建一个SSCCE,我们可以运行、修改和帮助纠正。稍后我将向您展示一个 SSCCE 示例。
    • @TomRichardson:SSCCE 已发布。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-03-06
    • 1970-01-01
    • 2010-10-29
    • 2010-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多