2009-10-01 222 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外观作者&的感觉)。

三叉戟应该帮助你解决不同物体以不同速度移动的问题,而不必为每个物体创建一个线程(这是最终的问题)。