TomRichardson
TomRichardson

Reputation: 6033

Flickering images in JPanel

I have created a game which paints images to a JPanel. This runs with a JTimer which executes every 14 milliseconds to move the background, moves obstacles and moves the player (helicopter). I get flickering of the background and of the obstacles occasionally, and it does not run particularly smoothly. Is there a way I can get around this? Do I need to use threads and animate in a different class? I also used double buffering, but then I found out that JPanels are double buffered anyway, so I removed it.

Here is the code from the Panel class.

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

    }
}

Sorry about all the code, as you may be able to tell, I am new to Java and programming in general (started last year at college), but it is something I wish to progress with and make a career out of.

Thanks Tom

Upvotes: 1

Views: 983

Answers (2)

Hovercraft Full Of Eels
Hovercraft Full Of Eels

Reputation: 285415

You're trying to do AWT graphics in a Swing program -- don't do this. Instead there's no place for update in a Swing graphics program and you should not be overriding the paint method but rather the JPanel's paintComponent method. This way you'll take advantage of Swing's double buffering.

Here's an SSCCE that shows AWT vs Swing animation, though unfortunately, I don't see much difference between the two as I'd hoped.

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();
         }
      });
   }

}

Upvotes: 2

camickr
camickr

Reputation: 324078

Don't use a TimerTask.

You should be using a Swing Timer. Then all updates to the GUI would be done on the Event Dispatch Thread.

Upvotes: 2

Related Questions