Bonjour,
J'essaie de coder une suite d'animations, mais je n'arrive pas à les enchaîner. J'ai fait un exemple assez simple pour illustrer mon problème.
Le principe de ce que je souhaite faire :
Dans mon panel, je clique à un endroit (une flèche dessinée), ce qui fait "descendre" un petit disque du haut en bas de ce panel.
Le programme fait ensuite descendre un second disque de haut en bas, à l'endroit qu'il aura choisi.
Mon problème : le premier disque descend bien (la première animation fonctionne), mais pas le second.
Voici mon code. J'ai codé mes classes pour essayer de suivre le pattern MVC.
Mon modèle :
Mon controler :
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 public class Model extends AbstractModel { protected static Model tmpModel; protected boolean animation = false; public Model(Color color1, Color color2) { this.color1 = color1; this.color2= color2; for (int j = 0 ; j < 7 ; j++) { column[j] = 0; } } public void setAnimation (boolean animation) { System.out.print("*** Méthode \"setAnimation\" de la classe Model ***"); System.out.println(" = > animation : " + animation); this.animation = animation; } @Override public int[] getColumn() { return this.column; } @Override public void setColumn(int[] column) { this.column = column; } @Override public Color getCurrentColor() { return this.currentColor; } @Override public void setCurrentColor(Color color) { this.currentColor= color; } public void reset() { for (int i = 0 ; i < 7 ; i ++) { column[i] = 0; } this.removeObserver(); } public Model getTmpModel() { return Model.tmpModel; } public void setTmpModel(Model tmpModel) { Model.tmpModel= tmpModel; } @Override public void addObserver(Observer obs) { this.listObserver.add(obs); } @Override public void removeObserver() { this.listObserver = new ArrayList<Observer>(); } @Override public void notifyObserver(Color color, int column) { System.out.println("*** Méthode \"notifyObserver\" de la classe Model ***"); for (Observer obs : listObserver) { obs.update(color, column); } } @Override public synchronized void move(Color color, int column, boolean mainModel) { System.out.println("\n*** Méthode \"move\" de la classe Model ***"); System.out.println("\tDébut du Thread \"moveThread\""); notifyObserver(color, column); System.out.println("\tFin du Thread \"moveThread\""); if (mainModel) { currentColor = (currentColor == color1) ? color2 : color1; int count = 0; System.out.println("\tanimation : " + animation + " => " + (animation == true ? "Début boucle d'attente" : "Pas de boucle d'attente")); while (animation) { try { Thread.sleep(500); count++; System.out.println("Boucle d'attente n°" + count); } catch (InterruptedException e) { e.printStackTrace(); } } if (currentColor == color2) { System.out.println("\tAppel à la méthode chooseNextMove"); this.chooseNextMove(); } } } public void chooseNextMove() { System.out.println("*** Méthode \"chooseNextMove\" de la classe Model ***"); this.move(currentColor, 3, true); //J'ai simplifié le code ici en choisissant l'entier 3. En réalité, il y a différents calculs à la place. } }
Ma frame :
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 public class Controler extends AbstractControler { protected Model model; protected boolean animation = false; public Controler(Model model) { super(model); this.model = model; } @Override public void control(int i) { this.model.move(this.model.getCurrentColor(), i, true); } public void setAnimation (boolean animation) { System.out.print("*** Méthode \"setAnimation\" de la classe Controler ***"); System.out.println(" = > animation : " + animation); this.animation = animation; this.model.setAnimation(animation); } }
Mon panel :
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 public class Frame extends JFrame implements Observer { private JPanel container; private Panel panel; private Model model; private Dimension dimension; private String name; public Frame(Model model, Controler controler) { this.setSize(800, 800); this.setLocationRelativeTo(null); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.name = "Fenêtre"; this.model = model; this.dimension = new Dimension(this.getWidth(), this.getHeight()); this.container = new JPanel(); this.container.setBackground(Color.white); this.container.setPreferredSize(this.dimension); panel = new Panel(controler); this.container.add(panel, BorderLayout.CENTER); this.setContentPane(this.container); this.setVisible(true); } public Panel getPanel() { return this.panel; } public String toString() { return this.name; } @Override public void update(Color color, int column) { // TODO Auto-generated method stub } }
Ma classe Main :
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216 public class Panel extends JPanel implements Observer, MouseListener, MouseMotionListener { private int length = 80; private int gap = 6; private Point2D origin = new Point2D.Double(20, 20); private Ellipse2D movingPiece = new Ellipse2D.Double(); //disque en train de descendre private Point2D movingPieceOrigin = new Point2D.Double(); //coordonnées origine du disque en train de descendre private Color movingPieceColor; //Couleur du disque en train de descendre private int column[] = new int[7]; private GeneralPath arrow[] = new GeneralPath[7]; private Point2D arrowOrigin[] = new Point2D[7]; private boolean isMoved[] = {false, false, false, false, false, false, false}; private boolean isClicked[] = {false, false, false, false, false, false, false}; private Controler controler; private String name; private boolean animation = false; public Panel(Controler controler) { this.setPreferredSize(new Dimension(700, 750)); initPanel(); this.addMouseListener(this); this.addMouseMotionListener(this); this.controler = controler; this.name = "Panel"; } public String toString() { return this.name; } public void initPanel() { for (int i = 0 ; i < 7 ; i ++) { column[i] = 0; arrowOrigin[i] = new Point2D.Double(origin.getX() + gap + i * (length + gap), origin.getY()); arrow[i] = new GeneralPath(); } movingPieceOrigin = new Point2D.Double(0, - length - 1); } public void paintComponent(Graphics g) { Graphics2D g2d = (Graphics2D)g; g2d.setPaint(new Color(234, 234, 255)); g2d.fillRect(0, 0, this.getWidth(), this.getHeight()); g2d.setPaint(Color.black); g2d.setStroke(new BasicStroke(1)); for (int i = 0 ; i < 7 ; i++) { arrow[i].moveTo(arrowOrigin[i].getX() + 20, arrowOrigin[i].getY()); arrow[i].lineTo(arrowOrigin[i].getX() + 3 * 20, arrowOrigin[i].getY()); arrow[i].lineTo(arrowOrigin[i].getX() + 3 * 20, arrowOrigin[i].getY() + 3 * 40); arrow[i].lineTo(arrowOrigin[i].getX() + 80, arrowOrigin[i].getY() + 3 * 40); arrow[i].lineTo(arrowOrigin[i].getX() + 40, arrowOrigin[i].getY() + 2 * 80); arrow[i].lineTo(arrowOrigin[i].getX(), arrowOrigin[i].getY() + 3 * 40); arrow[i].lineTo(arrowOrigin[i].getX() + 20, arrowOrigin[i].getY() + 3 * 40); arrow[i].closePath(); if (isClicked[i] == true) g2d.setPaint(Color.yellow); else if (isMoved[i] == true) g2d.setPaint(Color.cyan); else g2d.setPaint(Color.white); g2d.fill(arrow[i]); g2d.setPaint(Color.black); if (isMoved[i] == true || isClicked[i] == true) g2d.setStroke(new BasicStroke(5, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); else g2d.setStroke(new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); g2d.draw(arrow[i]); g2d.setStroke(new BasicStroke(1)); } movingPiece = new Ellipse2D.Double(movingPieceOrigin.getX(), movingPieceOrigin.getY(), 85, 85); if (movingPieceOrigin.getY() > (- 1 ) * 80) { g2d.setPaint(Color.black); g2d.draw(movingPiece); g2d.setPaint(movingPieceColor); g2d.fill(movingPiece); g2d.setPaint(Color.black); } } @Override public void update(Color color, int column) { // TODO Auto-generated method stub System.out.println("*** Méthode \"update\" de la classe Panel ***"); movePiece(color, column); return; } public void movePiece(Color color, int column) { System.out.print("*** Méthode \"movePiece\" de la classe Panel ***"); System.out.println(" - animation : " + animation); if (!animation) { System.out.println("\t!animation : " + !animation + " => début animation"); animation = true; //this.controler.setAnimation(animation); Runnable runnable = new Runnable() { @Override public void run() { System.out.println("*** Méthode \"run\" du \"Runnable\" du Thread t de la méthode \"movePiece\" de la classe Panel ***"); try { int y = -80; movingPieceOrigin.setLocation(origin.getX() + gap / 2 + column * (length + gap), y); movingPieceColor = color; System.out.println("\tDébut de la boucle \"while\" => animation : " + animation); while(animation) { try { Thread.sleep(20); System.out.print(". "); } catch (InterruptedException e) { e.printStackTrace(); } y+=10; movingPieceOrigin.setLocation(origin.getX() + gap / 2 + column * (length + gap), y); repaint(); if (movingPieceOrigin.getY() >= 750) { System.out.println(""); animation = false; controler.setAnimation(animation); } } System.out.println("\tFin de la boucle \"while\""); y = - length; } finally { animation = false; controler.setAnimation(animation); } repaint(); System.out.println("\tAprès le dernier repaint"); } }; Thread t = new Thread(runnable, "movingPiece"); t.start(); } else { System.out.println("\t!animation : " + !animation + " => pas d'animation"); } System.out.print("*** Fin méthode \"movePiece\" de la classe GamePanel ***"); System.out.println(" - animation : " + animation); return; } public void setMovingPieceOrigin(Point2D origin) { this.movingPieceOrigin.setLocation(origin.getX(), origin.getY()); } @Override public void mouseClicked(MouseEvent e) { // TODO Auto-generated method stub for (int j = 0 ; j < 7 ; j++) { if (arrow[j].contains(new Point2D.Double(e.getX(), e.getY()))) { //System.out.println("\t\t\tCurseur dans la flèche " + j + " !"); isClicked[j] = true; try { this.controler.control(j); } catch (NullPointerException ex) { ex.printStackTrace(); System.out.println("\tj = " + j); } } else isClicked[j] = false; this.repaint(); } } @Override public void mouseEntered(MouseEvent e) { // TODO Auto-generated method stub } @Override public void mouseExited(MouseEvent e) { // TODO Auto-generated method stub for (int j = 0 ; j < 7 ; j++) { isClicked[j] = false; isMoved[j] = false; this.repaint(); } } @Override public void mousePressed(MouseEvent e) { // TODO Auto-generated method stub } @Override public void mouseReleased(MouseEvent e) { // TODO Auto-generated method stub } @Override public void mouseDragged(MouseEvent arg0) { // TODO Auto-generated method stub } @Override public void mouseMoved(MouseEvent e) { // TODO Auto-generated method stub boolean arrowChanged = false; for (int j = 0 ; j < 7 ; j++) { if (arrow[j].contains(new Point2D.Double(e.getX(), e.getY()))) { if (!isMoved[j]) { isMoved[j] = true; arrowChanged = true; } } else { if (isMoved[j]) { isMoved[j] = false; arrowChanged = true; } } } if (arrowChanged) { this.repaint(); arrowChanged = false; } } }
Et voici ce que j'obtiens en console lorsque je lance le programme et que je clique sur l'une des flèches :
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 public class Main { public static void main(String[] args) { // TODO Auto-generated method stub Color color1 = Color.red; Color color2 = Color.blue; Model model = new Model(color1, color2); model.setCurrentColor(color1); Model tmpModel = new Model(color1, color2); model.setTmpModel(tmpModel); Controler controler = new Controler(model); SwingUtilities.invokeLater(new Runnable() { public void run() { Frame frame = new Frame(model, controler); model.addObserver(frame.getPanel()); model.addObserver(frame); } }); } }
Donc, de ce que je comprends :*** Méthode "move" de la classe Model ***
Début du Thread "moveThread"
*** Méthode "notifyObserver" de la classe Model ***
*** Méthode "update" de la classe Panel ***
*** Méthode "movePiece" de la classe Panel *** - animation : false
!animation : true => début animation
*** Fin méthode "movePiece" de la classe GamePanel *** - animation : true
Fin du Thread "moveThread"
animation : false => Pas de boucle d'attente
Appel à la méthode chooseNextMove
*** Méthode "chooseNextMove" de la classe Model ***
*** Méthode "move" de la classe Model ***
Début du Thread "moveThread"
*** Méthode "notifyObserver" de la classe Model ***
*** Méthode "update" de la classe Panel ***
*** Méthode "movePiece" de la classe Panel *** - animation : true
!animation : false => pas d'animation
*** Fin méthode "movePiece" de la classe GamePanel *** - animation : true
Fin du Thread "moveThread"
animation : false => Pas de boucle d'attente
*** Méthode "run" du "Runnable" du Thread t de la méthode "movePiece" de la classe Panel ***
Début de la boucle "while" => animation : true
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
*** Méthode "setAnimation" de la classe Controler *** = > animation : false
*** Méthode "setAnimation" de la classe Model *** = > animation : false
Fin de la boucle "while"
*** Méthode "setAnimation" de la classe Controler *** = > animation : false
*** Méthode "setAnimation" de la classe Model *** = > animation : false
Après le dernier repaint
- Lorsque que je clique sur l'une des flèches, la méthode "mouseClicked" du Panel appelle la méthode "control" du Controler.
- Cette méthode appelle la méthode "move" du Model.
- La méthode "move" appelle la méthode "notifyObserver", toujours dans le Model.
- Cette dernière appelle la méthode "update" du Panel (la vue).
- La méthode "update" appelle la méthode "movePiece" du Panel ; cette méthode est là pour gérer l'animation avec le Thread t, mais celui-ci ne se lance pas tout de suite ! A mon avis, c'est à partir de là que
- Au lieu de cela, la méthode "move" du Model continue et appelle la méthode "chooseNextMove", sans passer par la boucle d'attente car le booléen "animation" est "false".
- La méthode "chooseNextMove" rappelle la méthode "move" du Modelel.
- Cela relance la suite d'appel aux méthodes "notifyObserver" (Model), "update" et "movePiece" (Panel), comme précédemment.
- C'est à ce moment-là seulement que le Thread t est démarré et sa méthode "run" éxécutée.
- La 1ère animation a bien lieu : un disque rouge descend du haut en bas du Panel ; pendant ce temps la console renvoie les ". . . . ".
- Puis, la méthode "setAnimation" du Controler est appelée pour passer le booléen "animation" à false.
- Idem pour la méthode "setAnimation" du Model.
- Ces méthodes sont appelées une 2e fois ; j'imagine que c'est suite au 2e appel à la méthode "movePiece", mais cette dernière n'a jamais lancé la 2e animation.
- Puis, fin du programme.
Donc, comment faire pour que la 1ère animation se lance bien ? Puis pour que la 2e se lance après que la programme l'a décidé ?
En gros, j'aimerais que la 1ère animation se lance et se finisse AVANT que la méthode "chooseNextMove" du Model ne soit appelée. Ainsi, lorsque cette dernière serait appelée, elle reprendrait puis la 2e animation se lancerait.
Comment marquer une pause et attendre la fin de l'animation avant de reprendre l'éxécution de la méthode ?
J'essayé de mettre des Threads à différents endroits, mais sans succès. J'ai l'impression que mon problème vient de différents threads lancés par les méthodes appelées entre elles.
Faut-il utiliser des Threads avec les méthodes "wait" et "notify" ? et si oui, comment ?
Ou la classe SwingWorker ? mais je n'ai pas compris comment elle fonctionne et si elle peut être utile ici.
Ou autre chose (j'ai lu des choses sur Executor ou des Semaphores sans bien comprendre comment l'appliquer à mon problème) ?
Je précise que je suis débutant en Java. J'espère avoir été clair sur mon problème. J'ai tout extrait d'un code plus long et plus complexe en le simplifiant au max pour l'exemple, il est possible que j'ai laissé des bouts de code incohérents ou inutiles. Je n'ai pas mis les classes abstraites ni les interfaces Observer et Observable pour ne pas surcharger, mais je les rajoute si besoin.
Merci d'avoir lu jusqu'ici !
Partager