2014-04-20 22 views
0

我正在研究一個簡單的「彈跳球」 - Java中的動畫。這個想法是,它首先會產生一個直線移動的球,直到碰到面板邊界,這會導致它像預期的那樣反彈。然後,您可以用鼠標點擊x,y位置產生更多的球。到現在爲止還挺好。如何用多個線程同時繪製雙緩衝Java動畫?

我的問題是,每個球開始自己的線程,並且每個線程以自己的間隔單獨繪製到面板中,導致面板像瘋了似的閃爍。我知道這樣的問題可以通過實施雙緩衝來解決,我已經讀過,但從來沒有用過我自己。

我想知道如何在這裏使用雙緩衝,如果有多個線程同時繪製可能是一個問題(或相反,甚至是常態)?

非常感謝!

下面的代碼:

import java.awt.*; 
import java.awt.event.*; 
import java.util.*; 
import javax.swing.*; 

class MyCanvas extends JPanel 
{ 
    MyCanvas() 
    { 
     setBackground(Color.white); 
     setForeground(Color.black); 
    } 

    public void paintComponent(Graphics g) 
    { 
     super.paintComponent(g);  
    } 

    public Dimension getMinimumSize() 
    { 
     return new Dimension(300,300); 
    } 

    public Dimension getPreferredSize() 
    { 
     return getMinimumSize(); 
    } 
} 

public class BouncingBalls extends JFrame  // main class 
{ 
    MyCanvas m_gamefield; 

    public BouncingBalls() 
    { 
     setLayout(new BorderLayout()); 
     m_gamefield = new MyCanvas(); 
     add("Center",m_gamefield); 

     m_gamefield.addMouseListener(new MeinMausAdapter()); 

     setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 
    } 

    public void letsgo() 
    { 
     Ball first = new Ball(m_gamefield,200,50); 
     first.start(); 
    } 

    class MeinMausAdapter extends MouseAdapter 
    { 
     public void mousePressed(MouseEvent e) 
     { 
      Ball next = new Ball(m_gamefield,e.getX(),e.getY()); 
      next.start(); 
     } 
    } 

    public static void main(String[] args) 
    { 
     BouncingBalls test = new BouncingBalls(); 
     test.setVisible(true); 
     test.pack(); 
     test.letsgo(); 
    } 
} 

class Ball extends Thread 
{ 
    JPanel m_display; 
    int m_xPos,m_yPos; 
    int m_dx = 2;   // Steps into direction x or y 
    int m_dy = 2; 

    Ball(JPanel c,int x,int y) 
    { 
     m_display = c; 
     m_xPos = x; 
     m_yPos = y; 
    } 

    public void run() 
    { 
     paintBall();   // Paint at starting position 

     while(isInterrupted() == false) 
     { 
      moveBall(); 

      try 
      { 
       sleep(20); 
      } 

      catch(InterruptedException e) 
      { 
       return; 
      } 
     } 
    } 

    void paintBall() 
    { 
     Graphics g = m_display.getGraphics(); 
     g.fillOval(m_xPos, m_yPos, 20, 20); 
     g.dispose(); 
    } 

    void moveBall() 
    { 
     int xNew, yNew; 
     Dimension m; 
     Graphics g; 

     g = m_display.getGraphics(); 
     m = m_display.getSize(); 
     xNew = m_xPos + m_dx; 
     yNew = m_yPos + m_dy; 

     // Collision detection with borders, "bouncing off": 

     if(xNew < 0) 
     { 
      xNew = 0; 
      m_dx = -m_dx; 
     } 

     if(xNew + 20 >= m.width) 
     { 
      xNew = m.width - 20; 
      m_dx = -m_dx; 
     } 

     if(yNew < 0) 
     { 
      yNew = 0; 
      m_dy = -m_dy; 
     } 

     if(yNew + 20 >= m.height) 
     { 
      yNew = m.height - 20; 
      m_dy = -m_dy; 
     } 

     g.setColor(m_display.getBackground());     // Erases last position by 
     g.fillRect(m_xPos-2, m_yPos-2, m_xPos+22, m_yPos+22); // painting over it in white 

     m_xPos = xNew; 
     m_yPos = yNew; 
     paintBall();   // paint new position of Ball 
     g.dispose(); 
    } 
} 
+0

由調用'做的getGraphics()'上'Component'在一個形式或其他遲早會打破任何繪畫。這根本不是你在AWT/Swing中繪畫的方式。你應該閱讀http://docs.oracle。com/javase/tutorial/uiswing/painting /。除此之外:每個球的一個線程是一個總的矯枉過正。一個線程可以處理1000個球。也許你應該解釋你想用這些線程來實現什麼。他們在這裏不會帶來任何好處。如果你有,也許,100000個球,並想通過10個線程來做*運動,那麼OK。但繪畫仍然由一個線程完成。 – Marco13

回答

0

與Swing JComponents繪製時不用擔心雙緩衝。它們默認是雙重緩衝。

您應該不是在不同的線程上創建每個球,而是爲動畫實現一個Swing Timer。詳見How to Use Swing timers。您可以看到一個很好的示例here,其中將球對象添加到球列表並以不同間隔呈現。

其他注意事項

  • 切勿使用組件getGraphics。所有的繪畫應該在傳遞給paintComponent方法的Graphics上下文中完成。我看到你有這個方法。用它。您可以在Ball類中使用draw方法來獲取Graphics參數,然後從paintComponent方法中調用該方法,並將Graphics方法傳遞給它。例子也可以在上面的鏈接中看到。

你可以看到更多的例子herehereherehereherehere

+1

非常感謝!我已經看過你的一些建議,並將新版本的動畫放在一起。我已在下面的答案中發佈了它。乾杯! – Mvin

0

感謝peeskillet的優秀參考資料,我通過使用Swing定時器改變了代碼。現在它縮短了許多,並且完全沒有使用多線程。另外,由於在實際繪製它們之前計算所有的球位置(在一次掃描repaint()而不是許多較小的位置),閃爍已經停止。

雖然,我仍然有點好奇爲什麼它被認爲是壞的形式使用getGraphics()。它總是會導致閃爍(我想象過的可以用一個額外的雙緩衝層去除)?如果它指導每一個繪畫行爲,那麼paintComponent()就會變得更復雜的動畫更臃腫?如果有人想知道,我還是比較新的。

下面是對於那些有興趣的新代碼:

import java.awt.*; 
import java.awt.event.*; 
import java.util.ArrayList; 
import java.util.List; 
import javax.swing.*; 

public class BouncingBalls extends JFrame  // main class 
{ 
    MyCanvas m_gamefield; 
    public ArrayList<Ball> balls; 
    public Timer timer = null; 

    public BouncingBalls() 
    { 
      setLayout(new BorderLayout()); 
     m_gamefield = new MyCanvas(); 
      add("Center",m_gamefield); 

      balls = new ArrayList<Ball>(); 

      timer = new Timer(30, new ActionListener() 
     { 
        public void actionPerformed(ActionEvent e) 
        { 
         for (Ball b : balls) 
         { 
           b.move(); 
          } 
          repaint(); 
         } 
       }); 

      m_gamefield.addMouseListener(new MeinMausAdapter()); 

      setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 
    } 

    class MeinMausAdapter extends MouseAdapter 
    { 
      public void mousePressed(MouseEvent e) 
      { 
       balls.add(new Ball(m_gamefield,e.getX(),e.getY())); 
      } 
    } 

    class MyCanvas extends JPanel 
    { 
     MyCanvas() 
     { 
      setBackground(Color.white); 
       setForeground(Color.black); 
     } 

     public void paintComponent(Graphics g) 
     { 
       super.paintComponent(g); 

       for (Ball b : balls) 
       { 
        b.draw(g); 
       }      
     } 

     public Dimension getMinimumSize() 
     { 
      return new Dimension(300,300); 
     } 

     public Dimension getPreferredSize() 
     { 
      return getMinimumSize(); 
     } 
    } 

    public void letsgo() 
    { 
     balls.add(new Ball(m_gamefield,200,50)); 
     timer.start(); 
     } 

     public static void main(String[] args) 
    { 
     BouncingBalls test = new BouncingBalls(); 
     test.setVisible(true); 
     test.pack(); 
     test.letsgo(); 
    }   
} 

class Ball 
{ 
    JPanel m_display; 
    int m_xPos,m_yPos; 
    int m_dx = 2;   // Steps into direction x or y 
    int m_dy = 2; 

    Ball(JPanel c,int x,int y) 
    { 
      m_display = c; 
      m_xPos = x; 
      m_yPos = y; 
    } 

    void draw(Graphics g) 
    { 
      g.fillOval(m_xPos, m_yPos, 20, 20); 
    } 

    void move() 
    { 
      int xNeu, yNeu; 
      Dimension m; 

      m = m_display.getSize(); 
     xNeu = m_xPos + m_dx; 
      yNeu = m_yPos + m_dy; 


     // Collision detection with borders, "bouncing off": 

      if(xNeu < 0) 
      { 
       xNeu = 0; 
       m_dx = -m_dx; 
      } 

      if(xNeu + 20 >= m.width) 
      { 
       xNeu = m.width - 20; 
       m_dx = -m_dx; 
      } 

      if(yNeu < 0) 
      { 
       yNeu = 0; 
       m_dy = -m_dy;  
      } 

      if(yNeu + 20 >= m.height) 
      { 
       yNeu = m.height - 20; 
       m_dy = -m_dy; 
      } 

      m_xPos = xNeu; 
      m_yPos = yNeu; 
    } 
}