Image ne dessine pas après le sommeil java


J'essaie d'avoir une image d'arrière-plan puis un personnage dessiné dessus. Mon code fonctionnait jusqu'à ce que j'ajoute le sommeil, je n'ai pas obtenu 1500 fps.

package sylvyrfysh.screen;

import javax.swing.*;
import java.awt.*;
import game.infos.Information;

public class ImageLoadDraw extends JFrame{
/**
 * 
 */
private static final long serialVersionUID = 1L;
public static void main(){
    DisplayMode dm=new DisplayMode(Information.sX,Information.sY,16,DisplayMode.REFRESH_RATE_UNKNOWN);
    ImageLoadDraw i = new ImageLoadDraw();
    i.run(dm);
}
public void run(DisplayMode dm){
    setBackground(Color.PINK);
    setForeground(Color.WHITE);
    setFont(new Font("Arial",Font.PLAIN,24));
    s=new Screen();
    try{
        loadpics();
        s.setFullScreen(dm,this);
        try{
            Thread.sleep(10000);
        }finally{
            doRun=false;
            s.restoreScreen();
        }
    }catch(Exception e){
        e.printStackTrace();
    }
}
private void loadpics() {
    bg=new ImageIcon("src/sylvyrfysh/screen/maze_icon.png").getImage();
    chara=new ImageIcon("src/sylvyrfysh/screen/char.png").getImage();
    repaint();
}
public void paint(Graphics g){
    if(g instanceof Graphics2D){
        Graphics2D g2=(Graphics2D)g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    }
    while(true&&doRun){
        g.drawImage(bg,0,0,null);
        g.drawImage(bg,0,480,null);
        g.drawImage(bg,360,0,null);
        g.drawImage(bg,360,480,null);
        g.drawImage(bg,720,0,null);
        g.drawImage(bg,720,480,null);
        g.drawImage(chara,imgX,imgY,null);
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
private Screen s;
public static int imgX=0;
private int imgY=525;
private Image bg,chara;
private Boolean doRun=true;
}

Des idées sur ce qui le cause et comment le réparer? Comme je l'ai dit, sans sommeil, cela fonctionne bien.

Author: mKorbel, 2013-10-24

3 answers

Swing est un framework à thread unique. C'est-à-dire que toutes les interactions et modifications de l'interface utilisateur doivent être exécutées dans le contenu du thread de répartition des événements, y compris les demandes de repeindre.

Tout ce qui arrête, bloque ou empêche ce thread de s'exécuter empêchera l'EDT de traiter de nouveaux événements, y compris les demandes de repeindre. Cela fera essentiellement apparaître que votre programme a été suspendu (ou a cessé de répondre), car il l'a fait.

Dans votre run méthode que vous appelez Thread.sleep. Cela empêche probablement l'EDT de traiter de nouveaux événements, mais parce que vous avez réellement appelé la méthode à partir du thread "principal", cela pourrait cependant fonctionner...

Dans votre méthode paint, vous avez une boucle infinie ET un appel Thread.sleep. Ceux-ci empêcheront l'EDT de s'exécuter, car paint est appelé depuis le contexte de l'EDT.

La peinture ne se produira pas toujours immédiatement, sur certains systèmes, jusqu'à ce que la méthode paint revienne, elle peut ne pas être poussée à l'appareil pour la sortie, donc, même l'idée de faire une boucle dans le paint, indépendamment du fait que cela causera des problèmes pour l'EDT, est une mauvaise idée de toute façon.

Contrairement à d'autres frameworks d'interface utilisateur, vous n'avez pas besoin d'implémenter une "boucle principale", Swing s'en charge pour vous.

Au lieu de cela, dans votre méthode paint, vous devez simplement peindre ce que vous devez peindre pour ce cycle et quitter.

Au lieu de cela, vous devriez faire quelque chose comme...

public void paint(Graphics g){
    // Painting is a complex series of chained methods, failing to call super.paint
    // to cause significant issues
    super.paint(g);
    // Graphics is guaranteed to be an instance of Graphics2D since I think 1.4
    // You should create a copy, so any changes you make are not carried onto the
    // next component, Graphics is shared between all the components being painted
    // in this paint cycle.
    Graphics2D g2=(Graphics2D)g.create();
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

    g2.drawImage(bg,0,0,null);
    g2.drawImage(bg,0,480,null);
    g2.drawImage(bg,360,0,null);
    g2.drawImage(bg,360,480,null);
    g2.drawImage(bg,720,0,null);
    g2.drawImage(bg,720,480,null);
    g2.drawImage(chara,imgX,imgY,null);
    // If you create, you should dispose of it...
    g2.dispose();
}

...au lieu de cela

Au Lieu de Thread.sleep vous devriez faire usage de quelque chose comme javax.swing.Timer, par exemple...

s=new Screen();
try{
    loadpics();
    s.setFullScreen(dm,this);
    Timer timer = new Timer(10000, new ActionListener() {
        public void actionPerformed(ActionEvent evt) {
            s.restoreScreen();
        }
    });
    timer.setRepeats(false);
    timer.start();
}catch(Exception e){
    e.printStackTrace();
}

Jetez un oeil à Concurrence dans Swing pour plus de détails.

Vous devez également éviter de surcharger les conteneurs de niveau supérieur et en particulier de surcharger paint. Peindre une série complexe d'appels de méthode enchaînés, chacun effectuant un travail particulier, s'accumulant les uns sur les autres pour produire un résultat final.

Au lieu de cela, vous devrait commencer par un composant personnalisé, s'étendant de JPanel par exemple, et remplacer sa méthode paintComponent, en vous assurant d'appeler super.paintComponent avant de faire votre propre peinture.

Jetez un oeil à Effectuer une peinture personnalisée pour plus de détails

 1
Author: MadProgrammer, 2013-10-23 22:22:52

Eh bien celui-ci Thread.sleep(10000); éteignez TOUT pendant 10 secondes. Ce n'est PAS ce que vous voulez. Même le faire pour 30 milisec (qui est ~30frames / sec) n'est pas ce que vous voulez, car il arrête même l'entrée, etc.

Vous devez utiliser la minuterie. Il exécute le timing dans un autre thread et il dort et ne réveille que ce thread automatiquement pour un nombre donné de milisec, donc cela n'affecte pas votre programme et il ne peut l'appeler qu'après 30milisec par exemple.

Encore pour avoir une bonne application, cette minuterie devrait avoir une valeur faible et vous devriez compter combien de temps passé à travers System.nanoTime() et par exemple repeindre une fois chaque 30milisec, mais lire l'entrée chaque 5milisec etc.

 0
Author: libik, 2013-10-23 21:40:00

JE PENSE-> C'est parce que vous devez appeler leloadpics() parallèle, essayez ceci

public void run(DisplayMode dm){
    setBackground(Color.PINK);
    setForeground(Color.WHITE);
    setFont(new Font("Arial",Font.PLAIN,24));
    s=new Screen();
        loadpics();
        s.setFullScreen(dm,this);
        try{
            new Thread(new Runnable() {
      @Override
      public void run() {try {Thread.sleep(1000);doRun=false;s.restoreScreen();} catch (Exception e) {}
      }
    }).start();
}

Définissant également le doRun comme volatil. private Boolean doRun=true;

 0
Author: , 2013-10-23 22:02:53