IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

2D Java Discussion :

animation qui ne s'anime pas


Sujet :

2D Java

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    95
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 95
    Par défaut animation qui ne s'anime pas
    Bonjour,
    j'ai un problème qui doit être assez classique.
    Le voici :
    Pour faire simple, je veux créer une petite animation, mettons un rectangle qui se déplace dans un JPanel. Sa position dépend de la numérotation d'étapes.
    J'ai une méthode dans une classe Anim qui trace le rectangle :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    public void traceRectangle(int etape, Graphics2D g2D)
    {
         g2D.draw(rectangle);
    }
    puis je redéfinie, comme il se doit, la méthode paintComponent de la classe JPanel comme ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    protected void paintComponent(Graphics g)
    {
            super.paintComponent(g);
            Graphics2D g2D = (Graphics2D) g;
     
            for(int etape = 1; etape <= 10; etape++)
            {
                anim.traceRectangle(etape, g2D);
                System.out.println("Etape = " + etape);
                Thread.sleep(1000);
             }
    }
    Je passe bien sûr sur le bloc try/catch que je n'ai pas mis pour simplifier.

    Donc quand je lance le prog, les messages : "Etape = 1 Etape = 2, ... Etape = 10, s'affichent bien à raison d'une étape par seconde comme prévu, en revanche, et c'est là mon problème, les rectangles ne s'affichent pas les uns après les autres mais les 10 d'un seul coup.
    Alors pourquoi cela fonctionne avec l'affichage des étapes et pas avec l'affichage des rectangles ?
    Et comment remédier simplement à ce problème ? ... sans créer de Thread (car ça je sais faire, ou à peu près).

    Merci pour vos réponses.
    Virgile

  2. #2
    Membre Expert
    Avatar de visiwi
    Profil pro
    Inscrit en
    Février 2008
    Messages
    1 052
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 1 052
    Par défaut
    Salut,

    Une voie possible assez simple est de :

    Tu doit séparer ce qui d'un coté représente une modélisation (des calculs), même simple, comme par exemple dire que régulièrement on doit déplacer de tant de pixels tel objet. Et de l'autre l'affichage de ces objets.
    Cela t'amène a un objet possédant deux méthodes l'une chargée de calculer et mettre à jour tes objets d'un point de vue mathématiques (des données), et l'autre chargée de les dessiner (avec un objet Graphics) à l'écran.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class Game {
     
          public void calcul() {
                // calculs
          }
     
          public void dessine(Graphics g) {
                // peindre les objets
          }
     
    }
    Ensuite, il te faut passer par un Thread (ou un dérivé), sinon ton appli sera bloqué. Ce Thread sera charger d'appeler régulièrement d'abord la méthode calcul, puis de dessiner les objets. Tu peut avantageusement utiliser un Timer.

    Le fait "d'endormir" ton paint par un sleep(n) est à proscrire. En raison du fonctionnement de l'EDT (chargé de l'affichage en Swing), cela fait que tout s'affiche d'un coupt. C'est une notion importante et tu trouvera de la doc sur le sujet sur le site (EDT = EventDispatchThread).

    Voici un exemple simple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
     
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.Timer;
     
    // le jeu qui est un panel
    public class Game extends JPanel {
     
    	public static void main(String[] args) {
    		JFrame frame = new JFrame();
    		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		frame.setContentPane(new Game());
    		frame.pack();
    		frame.setLocationRelativeTo(null);
    		frame.setVisible(true);
    	}
     
    	private final static int FRAME_RATE = 50; // fps (on aura environ 50 cycle d'animations toutes les secondes), cela conditionne la vitesse général du jeu (donc de tous les objets animés)
    	private final static int TIMER_TIME = 1000 / FRAME_RATE;// en millisecondes (soit 1 secondes diviser par fps)
     
    	private Timer timer;
    	private Balle balle;
    	private int step;
     
    	public Game() {
    		setPreferredSize(new Dimension(200, 200));
    		step = 3; // en pixel (cela conditionne uniquement la vitesse de la balle)
    		balle = new Balle();
    		// la méthode actionPerformed sera appeler toutes les TIMER_TIME fois millisecondes
    		timer = new Timer(TIMER_TIME, new ActionListener() {
    			@Override
    			public void actionPerformed(ActionEvent e) {
    				compute();
    				repaint();
    			}			
    		});
    		timer.start(); // démmarrer le timer
    	}
     
    	// calcul les nouvelles coordonnées des objets, dans ce cas il n'y en a qu'un
    	public void compute() {
    		int newX = balle.getX() + step; // nouvelle coordonneé en x
    		// on vérifie que la nouvelle coordonnée ne touche pas un bord
    		if (newX < 0) {
    			newX = 0; // empêche la balle de sortir du cadre gauche
    			step = -step;  // inverser le sens de déplacement de la balle
    		} else if (newX + balle.getDiametre() > getWidth()) {
    			newX = getWidth() - balle.getDiametre(); // empêche la balle de sortir du cadre droit
    			step = -step; // inverser le sens de déplacement de la balle
    		}
    		balle.setX(newX); // fixer la nouvelle coordonnée en x
    	}
     
    	// dessine les objets, dans ce cas il n'y en a qu'un
    	@Override
    	protected void paintComponent(Graphics g) {
    		super.paintComponent(g);
    		balle.draw(g);
    	}
     
    }
     
    // un objet représentan le modèle d'une balle avec une méthode pour la dessiner
    class Balle {
     
    	private int diametre;
    	private int x;
    	private int y;
     
    	public Balle() {
    		x = 0;
    		y = 50;
    		diametre = 30;
    	}
     
    	public int getDiametre() {
    		return diametre;
    	}
     
    	public void setDiametre(int diametre) {
    		this.diametre = diametre;
    	}
     
    	public int getX() {
    		return x;
    	}
     
    	public void setX(int x) {
    		this.x = x;
    	}
     
    	public int getY() {
    		return y;
    	}
     
    	public void setY(int y) {
    		this.y = y;
    	}
     
    	public void draw(Graphics g) {
    		g.fillOval(x, y, diametre, diametre);
    	}
     
    }
    En espérant que cela puisse te faire avancer...
    N'hésite pas si tu as des questions.
    @+

  3. #3
    Membre confirmé
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    95
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 95
    Par défaut Ok. merci beaucoup mais regardez ceci
    Je ne comprend pas pourquoi l'animation marche dans ce cas-là, alors que ça ressemble fortement à mon cas (qui lui, ne marche pas).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
     
    import java.awt.Color;
    import java.awt.Graphics;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
     
     
     
    public class Main {
     
        int x = 70, y = 70;
     
        public static void main(String[] args) {
            Main ihm = new Main();
            ihm.go();
        }
     
        public void go() {
            JFrame cadre = new JFrame();
            cadre.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     
            MonPanneau panneau = new MonPanneau();
     
            cadre.getContentPane().add(panneau);
            cadre.setSize(300, 300);
            cadre.setVisible(true);
     
            for(int i = 0; i < 130; i++) {
                x++; y++;
                panneau.repaint();
     
                try{
                    Thread.sleep(50);
                } catch (Exception e){
                }
            }
     
        }
     
        class MonPanneau extends JPanel {
     
            @Override
            protected void paintComponent(Graphics g)
            {
                super.paintComponent(g);
                g.setColor(Color.WHITE);
                g.fillRect(0, 0, this.getWidth(), this.getHeight());
     
                g.setColor(Color.GREEN);
                g.fillOval(x, y, 40, 40);
            }
     
     
        }
     
    }

  4. #4
    Membre Expert
    Avatar de visiwi
    Profil pro
    Inscrit en
    Février 2008
    Messages
    1 052
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 1 052
    Par défaut
    Citation Envoyé par Virgile le chat Voir le message
    Je ne comprend pas pourquoi l'animation marche dans ce cas-là, alors que ça ressemble fortement à mon cas (qui lui, ne marche pas).
    Mais il fonctionne (du moins ton cercle se déplace) mais ce n'est pourtant pas la même chose. Ton appli est bloqué le temps de l'animation.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    for(int i = 0; i < 130; i++) {
                x++; y++;
                panneau.repaint();
     
                try{
                    Thread.sleep(50);
                } catch (Exception e){
                }
            }

  5. #5
    Rédacteur/Modérateur

    Avatar de bouye
    Homme Profil pro
    Information Technologies Specialist (Scientific Computing)
    Inscrit en
    Août 2005
    Messages
    6 904
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : Nouvelle-Calédonie

    Informations professionnelles :
    Activité : Information Technologies Specialist (Scientific Computing)
    Secteur : Agroalimentaire - Agriculture

    Informations forums :
    Inscription : Août 2005
    Messages : 6 904
    Billets dans le blog
    54
    Par défaut
    L'EDT est une Thread qui, dans l'AWT ou dans Swing, s'occupe a la fois de dessiner et de propager les evemements. Si tu la bloques sur un long calcul, elle ne fera ni l'un, ni l'autre :
    - Quand tu fais un sleep() ou une boucle dans le paintComponent(), ton appli ne fait rien d'autre durant ce temps (elle ne se reaffiche pas).
    - Et c'est pareil quand tu fais ce genre ces trucs dans un listenener quelconque.
    C'est ainsi que fonctionne l'EDT, c'est une Thread unique.

    Donc ton sleep() ou ta boucle doit etre fait dans une autre Thread et appeller repaint() de maniere a ce que l'EDT puisse dessiner les etapes intermediaires (un jeu ou un moteur d'animation plus precis utilisera des methodes de timing plus complexes qui tiennent compte du FPS, du taux de rafraichissement ou du temps passe a rendre une frame par exemple pour essayer d'avoir l'animation la plus fluide possible).

    Lancer une autre Thread pour faire un travail lourd c'est facile, tu peux utiliser :
    - Thread
    - java.util.Timer (a ne pas confondre avec javax.swing.Timer qui lui fait des taches dans l'EDT).
    - SwingWorker

    Mais il faut alors faire attention a l'interraction entre cette nouvelle Thread et l'EDT. Pas de probleme quand on appelle juste repaint() (repaint() est une methode qui retourne immediatement et indique juste qu'il faut repeindre plus tard) ou dans ton cas juste modifier les coordonnees de ta forme. Par contre pour appeler des trucs du genre myJTable.setXXXX, ces appels doivent exclusivement se faire sur l'EDT et donc il faut utiliser SwingUtilities.invokeLater() pour mettre a jour la GUI dans la bonne Thread.
    Merci de penser au tag quand une réponse a été apportée à votre question. Aucune réponse ne sera donnée à des messages privés portant sur des questions d'ordre technique. Les forums sont là pour que vous y postiez publiquement vos problèmes.

    suivez mon blog sur Développez.

    Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the universe trying to produce bigger and better idiots. So far, the universe is winning. ~ Rich Cook

  6. #6
    Membre confirmé
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    95
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 95
    Par défaut merci
    Merci pour ces très intéressantes réponses.
    Mais j'ai du mal à comprendre le fonctionnement du Thread EDT.
    Par exemple, avec mon exemple ci-dessus (la boule qui se déplace), si j'ai bien compris ce que vous m'avez expliqué, ça fonctionne car il y a deux Thread (le Thread Main et l'EDT). Le Thread main exécute main et l'edt s'occupe de l'affichage. Mais j'ai modifié légèrement ce petit programme test en ajoutant un bouton à la fenêtre et en mettant et en mettant dans actionPerformed (méthode appelée lors du clic sur le bouton) le code qui éxécutait le déplacement de la boule. Voici une portion du code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     
    JButton go  = new JButton("GO !");
     
            go.addActionListener(new ActionListener() {
     
                public void actionPerformed(ActionEvent e)
                {
                    for(int i = 0; i < 130; i++) {
                                x++; y++;
                                System.out.println("Avant repaint " + Thread.currentThread().getName());
     
                                panneau.repaint();
     
                                System.out.println("Après repaint" + Thread.currentThread().getName());
     
                                try{
                                    Thread.sleep(50);
                                } catch (Exception exp){
                                }
                            }
                }
            });
    Et là, le clic sur le bouton ne fait plus déplacer la boule !!!
    La boucle s'éxécute bien puisque j'ai une série d'affichages du type :
    Avant repaint : AWT- event queue
    Après repaint : AWT- event queue

    Que se passe-t-il donc ?
    La demande de rafraichissement de l'affichage (repaint) n'est plus prise en compte dans ce cas-là. Je suppose que c'est parce qu'elle est à l'intérieur de la méthode actionPerformed (seule possibilité!). Bon ok. Et alors ???
    Donc si j'ai bien compris, le repaint n'est pas exécuté parce qu'il est exécuté par le thread EDT et que celui-ci est en train d'éxécuter actionPerformed ?
    Et pourquoi ???
    Pourquoi ce thread n'exécute pas repaint même s'il est en train d'éxécuter actionPerformed ? Quel rapport ?

    Désolé d'être aussi insistant mais je déteste ne pas comprendre précisémment les choses. Et ces histoires de Thread EDT m'ont l'air d'avoir un fonctionnement un peu spécial.
    Merci beaucoup.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Animation flash qui ne s'affiche pas en ligne
    Par Thierry_A dans le forum Flash
    Réponses: 1
    Dernier message: 21/09/2008, 11h27
  2. Réponses: 2
    Dernier message: 28/10/2007, 17h46
  3. gif animé qui ne s"affiche pas sous IE
    Par manaboko dans le forum Balisage (X)HTML et validation W3C
    Réponses: 3
    Dernier message: 10/08/2007, 16h11
  4. Réponses: 1
    Dernier message: 21/11/2006, 21h56
  5. Un Gif animé qui ne se fige pas lors des traitements ?
    Par delphi+ dans le forum Composants VCL
    Réponses: 11
    Dernier message: 18/03/2006, 10h31

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo