2011-10-11 90 views
1

我创建了一个将图像绘制到JPanel的游戏。这是通过每隔14毫秒执行一次JTimer来移动背景,移动障碍物并移动玩家(直升机)的。偶尔会出现背景和障碍物的闪烁,并且运行不顺畅。 有没有办法解决这个问题?我是否需要在不同的课程中使用线程和动画? 我也使用双缓冲,但后来我发现JPanels是双缓冲,所以我删除它。在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和总体规划(在大学从去年开始),但它是我希望与进步做出了职业生涯。

感谢 汤姆

回答

2

不要使用TimerTask。

您应该使用摆动计时器。然后,GUI上的所有更新都将在Event Dispatch Thread上完成。

+0

我已经实现了一个Swing Timer,但我只找到了与ActionListener一起使用的示例。当我使用这个例子时,只要鼠标移动就重绘。即使在Game Over屏幕上,我也不希望这种情况继续发生。 – TomRichardson

+0

没关系,修好!感谢您的信息!这是我移动一些代码的方式的一个问题。 – TomRichardson

2

你试图做一个Swing程序AWT图形 - 不这样做。相反,在Swing图形程序中没有更新的地方,您不应该重写paint方法,而应该重写JPanel的paintComponent方法。这样你就可以利用Swing的双缓冲。

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

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

+1,请参阅[在AWT和Swing中绘制](http://java.sun.com/products/jfc/tsc/articles/painting/index.html)中的差异。 – camickr

+0

谢谢你的建议!我通过将paint()方法更改为paintComponent()来更改我的代码,并且删除了update()方法。然而,它仍然有点摇摇晃晃(图像昙花一现,背景+图像对运动有一点影响)。有没有办法来解决这个问题? 谢谢! – TomRichardson

+0

很难说给定您的代码的大小以及我们无法编译或运行代码的事实。我建议你缩小它,创建一个[SSCCE](http://sscce.org),我们可以运行,修改和帮助纠正。我会向你们展示一些SSCCE的例子。 –