2009-10-01 3 views
1

mon objectif final pour cette application est d'animer plusieurs éléments dans le même JPanel à une vitesse différente en utilisant un fil pour chaque élément.la première partie est faite mais les éléments bougent au même vitesse et je n'ai aucune idée sur la façon de résoudre ce problème.multithread avec java swing pour une animation 2d simple

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


} 

Voici la classe d'étoiles qui est l'objet en mouvement.

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; 
     */ 
    } 


} 

et voici le squelette de l'animation qui s'étend un 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(); 
    } 
} 

avez-vous une idée de comment atteindre mes objectifs, je suis l'aide de fils bien insérés? merci d'avance.

Répondre

1

Vous devriez peindre dans AWTDispatchThread. Pour ce faire, vous voudrez utiliser quelque chose comme SwingUtilities.invokeLater(Runnable); Ceci s'applique non seulement à votre animation, mais aussi à la création et au réglage de votre JFrame. Ne pas le faire pourrait entraîner des blocages avec le fil de peinture. En outre, lorsque vous déplacez vos opérations de peinture dans les méthodes SwingUtilites, vous ne voulez pas inclure de boucles while(true), car cela réquisitionnera votre fil de peinture.

+0

"votre carte n'est pas un Runnable, donc votre animateur = nouveau Thread (ceci) ne devrait pas compiler." euh ... oui c'est le cas. 'public class Board extends JPanel implémente Runnable' – Powerlord

+0

Oui, j'ai édité pendant que vous écriviez votre commentaire. Je l'ai vu une fois que vous avez reformaté. Dommage que nous puissions upvoter des modifications de publication. – akf

-1

si vous êtes sûr que vous voulez peindre dans les fils, vous pouvez utiliser:

update(getGraphics()); 

au lieu de repeindre. cela est généralement considéré comme une mauvaise pratique, car vous peignez normalement des choses dans le fil AWT.

+0

@Vous avez raison à ce sujet, mais ce n'est pas la raison pour laquelle ils se déplacent à la même vitesse. – OscarRyz

+0

à droite. Je pense que votre problème est que vous dormez un temps constant pour chaque itération. ce que vous voulez est de déplacer votre objet une distance variable (vous pouvez passer dx et dy dans le ctor) et dormez un temps constant (disons 40ms pour 25 fps). –

1

Généralement, les composants Swing doivent être utilisés à partir du fil d'expédition d'événement AWT (EDT). repaint est l'une des méthodes qui est censé être utilisé avec EDT. Cependant, votre Star n'est pas et ne devrait pas être thread-safe.

L'approche la plus simple consiste à utiliser EDT uniquement (au moins pour commencer). Au lieu d'utiliser Thread, utilisez javax.swing.Timer qui se déclenche sur l'EDT. Commentaires divers: Votre méthode paint ne devrait pas avoir besoin de disposer de l'objet graphique qui lui a été envoyé ou de la synchroniser avec Toolkit. Le composant n'a pas besoin d'être mis en double-tampon, mais doit être défini comme opaque (JPanel n'est pas garanti d'être opaque). Vous devriez simplement étendre JComponent au lieu de JPanel, car ce n'est pas un panneau. Ce n'est généralement pas une bonne idée pour les classes externes de mettre en œuvre Runnable. Préférez les variables privées.

2

C'est parce que vous appeler la méthode « move » à un taux fixe déterminé par le retard de la première « start »

Thread.sleep(star.delay); 

Donc, si vous les déplacez un peu tous les « n » millisecondes, ils semblera bouger à la même paix.

Si vous voulez qu'ils se déplacent à des vitesses différentes, vous devez les déplacer dans différents fil (vous utilisez un seul maintenant) Gardez à l'esprit le commentaire par Omry,

EDIT

I J'ai fait quelque chose de similaire tout récemment

J'ai deux choses différentes qui m'animent donc j'ai deux temporisateurs (les temporisateurs utilisent des threads en dessous, mais ils peuvent répéter le code d'exécution à chaque fréquence fixe).

La première application du texte à un JLabel chaque seconde (1000 ms)

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

Et autre modification de l'image afficher toutes les 10 secondes (10.000 ms)

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

J'ai une vidéo de le résultat:

enter image description here

Pour une gestion plus avancée (et agréable) du temps, vous DEVEZ jeter un oeil au projet "Timing Framework" qui ajoute des fonctionnalités supplémentaires aux temporisateurs.

+0

+1 pour avoir répondu à la question. :) Pourrait utiliser le fil unique, mais utilisez, dites une file d'attente prioritaire pour trouver l'étoile suivante à déplacer, ou un autre algorithme aléatoire (comme calculer la position à partir de l'heure, plutôt que de stocker la position). –

1

Je vous suggère de jeter un oeil à la bibliothèque open source trident qui ne vient que, son auteur, Kirill Grouchnikov est bien connu dans le monde Swing (il est l'auteur du célèbre regard substance & sensation). Trident devrait vous aider à résoudre le problème du déplacement de différents objets à des vitesses différentes, sans avoir à créer un seul thread par objet (ce qui est un problème à la fin).