2009-10-01 109 views
1

我的這個應用程序的最終目標是使用一個線程以不同的速度動畫在同一JPanel的幾個項目每個item.the第一部分已經完成,不過項目在同一移動速度,我不知道如何解決這個問題。多線程與一個簡單的2D動畫的Java Swing

package javagamestutos; 



import java.awt.Color; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.Toolkit; 
import java.util.ArrayList; 
import java.util.logging.Level; 
import java.util.logging.Logger; 
import javax.swing.JPanel; 


public class Board extends JPanel implements Runnable { 


    private Star star; 
    private Thread animator; 
    ArrayList<Star> items=new ArrayList<Star>(); 


    public Board() { 
     setBackground(Color.BLACK); 
     setDoubleBuffered(true); 
     star=new Star(25,0,0); 
     Star star2=new Star(50,20,25); 
     items.add(star2); 
     items.add(star); 
    } 

    public void addNotify() { 
     super.addNotify(); 
     animator = new Thread(this); 
     animator.start(); 
    } 

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

     Graphics2D g2d = (Graphics2D)g; 



     for (Star s : this.items) { 
      g2d.drawImage(s.starImage, s.x, s.y, this); 
     } 


     Toolkit.getDefaultToolkit().sync(); 
     g.dispose(); 
    } 

    public void run() { 

     while(true){ 
      try { 
       for (Star s : this.items) { 
        s.move(); 
       } 
       repaint(); 

       Thread.sleep(star.delay); 
      } catch (InterruptedException ex) { 
       Logger.getLogger(Board.class.getName()).log(Level.SEVERE, null, ex); 
      } 
     } 
    } 


} 

這裏是星級,這是移動的項目。

package javagamestutos; 

import java.awt.Image; 
import javax.swing.ImageIcon; 

/** 
* 
* @author fenec 
*/ 
public class Star { 
    Image starImage; 
    int x,y; 
    int destinationX=200,destinationY=226; 
    boolean lockY=true; 
    int delay; 


    public Star(int delay,int initialX,int initialY){ 
     ImageIcon ii = new ImageIcon(this.getClass().getResource("star.png")); 
     starImage = ii.getImage(); 
     x=initialX; 
     y=initialY; 
     this.delay=delay; 
    } 


    void moveToX(int destX){ 
     this.x += 1; 
    } 


    boolean validDestinatonX(){ 
     if(this.x==this.destinationX){ 
       this.lockY=false; 
       return true; 
      } 
     else 
      return false; 
    } 

    void moveToY(int destY){ 
     this.y += 1; 
    } 


    boolean validDestinatonY(){ 
     if(this.y==this.destinationY) 
      return true; 
     else 
      return false; 
    } 

    void move(){ 

     if(!this.validDestinatonX()) 
      x+=1; 
     if(!this.validDestinatonY() && !this.lockY) 
      y+=1; 

     /*if(!this.validDestinatonY()) 
      y+=1; 
     */ 
    } 


} 

這裏是擴展一個JFrame動畫的骨架:

package javagamestutos; 
import javax.swing.JFrame; 

public class Skeleton extends JFrame { 

public Skeleton() { 
     add(new Board()); 
     setTitle("Stars"); 
     setDefaultCloseOperation(EXIT_ON_CLOSE); 
     setSize(300, 280); 
     setLocationRelativeTo(null); 
     setVisible(true); 
     setResizable(false); 
    } 
    public static void main(String[] args) { 
     new Skeleton(); 
    } 
} 

你有任何想法如何實現我的目標我使用的線程proprely? 預先感謝您。

回答

1

你應該在AWTDispatchThread來畫。要做到這一點,您將需要使用類似SwingUtilities.invokeLater(Runnable);這不僅適用於您的動畫,還適用於您的JFrame的創建和設置可見。未能這樣做可能會導致繪畫線程死鎖。此外,將繪畫操作移至SwingUtilites方法時,您不會希望包含任何while(true)循環,因爲這會佔用您的繪畫線。

+0

「你的Board不是一個Runnable,所以你的animator = new Thread(this)不應該編譯。」 呃...是的。 '公共類板擴展JPanel實現Runnable' – Powerlord 2009-10-01 18:04:04

+0

是的,我編輯,因爲你正在輸入你的評論。一旦您重新格式化,我就會看到它。太糟糕了,我們可以upvote後編輯。 – akf 2009-10-01 18:11:43

-1

如果您確定要在主題畫,你可以使用:

update(getGraphics()); 

,而不是重繪。 這通常被認爲是不好的做法,因爲你通常在AWT線程中繪製東西。

+0

@你說得對,但這不是他們以相同速度移動的原因。 – OscarRyz 2009-10-01 18:07:46

+0

沒錯。 我認爲你的問題在於你每次迭代都會保持一個恆定的時間。 你想要的是移動你的物體一個可變的距離(你可以傳遞dx和dy在ctor中)並且睡眠一個固定的時間(比如25ms的時候是40ms)。 – 2009-10-01 18:17:48

1

一般Swing組件應該從AWT事件指派線程(EDT)中使用。 repaint是可以使用EDT的方法之一。但是,您的Star不是,也不應該是線程安全的。

最簡單的方法是去EDT-只(至少與啓動)。而不是使用Thread使用javax.swing.Timer在EDT上觸發。

其他評論:應該不需要您的paint方法來處理髮送給它的圖形對象,或使用Toolkit進行同步。該組件不需要設置爲雙緩衝,但應設置爲不透明(不保證不透明)。您應該擴大JComponent而不是JPanel,因爲這不是面板。外部類實施Runnable通常不是一個好主意。偏好私有變量。

2

那是因爲你是在調用的「移動」法在第一的延遲指定的固定利率「開始」

Thread.sleep(star.delay); 

所以,如果你移動他們一點點的每一個「N」毫秒,他們似乎會在相同的和平中移動。

如果你希望他們以不同的速度移動,你必須將它們移動在不同的線程(使用的是現在只有一個)。請記住由omry的評論,

編輯

我最近做了類似的事情

我有兩個不同的東西,所以我有兩個計時器(定時器使用下面的線程,但他們可以重複每個固定速率的執行代碼)。

第一個申請文本到JLabel每秒(1000毫秒)

final Timer timer = new Timer(); 
    timer.scheduleAtFixedRate(new TimerTask() { 
     public void run(){  
      setText(); 
     } 
    }, 0, 1000); 

與其他改變顯示圖像每10秒(10,000毫秒)

final Timer imageTimer = new Timer(); 
    imageTimer.scheduleAtFixedRate(new TimerTask() { 
     public void run() { 
      setImage(); 
     } 
    }, 0, 10000); 

我的視頻結果在這裏:

enter image description here

對於更先進(和更好)的時間管理,你必須看看"Timing Framework"項目,它爲定時器增加了額外的功能。

+0

用於回答問題。 :)可以使用單個線程,但可以使用優先級隊列來查找要移動的下一顆恆星,或使用其他隨機算法(例如從時間計算位置,而不是存儲位置)。 – 2009-10-01 18:15:05

1

我建議你看看開放源代碼庫trident,它的確如此,其作者Kirill Grouchnikov在Swing世界中是衆所周知的(他是着名Substance外觀作者&的感覺)。

三叉戟應該幫助你解決不同物體以不同速度移動的問題,而不必爲每個物體創建一個線程(這是最終的問題)。