Donc je serais mieux de refaire un autre jeu ?
Donc je serais mieux de refaire un autre jeu ?
Bah, ça dépend. C'est quoi le but de ce projet. C'est quoi qu'on vous demande au juste ? C'est déjà assez bizarre pour moi de voir que tu intègres juste un jeu fait par d'autres dans un projet. C'est peut-être ce qu'on vous demande, mais j'aurais trouvé plus logique qu'on vous demande de le coder vous même. Surtout qu'en théorie, tu n'as pas le droit le faire si les auteurs ne l’autorise pas. Un code open source n'est pas forcément libre.
Ensuite, faut voir si ça te gêne ou pas. Au pire, si le jeu tourne en mémoire, ça consomme du cpu et de la mémoire. Si tu le lances une fois, ce n'est pas très important. Et pour la stacktrace, ça peut s'expliquer. Tu intègres un jeu qui n'est pas fait pour l'être.
Tu as aussi l'option de le faire tourner comme un programme externe, comme ça il n'aura pas d'influence sur ton programme, et il tournera dans sa JVM comme c'est prévu.
Ou tu peux choisir un autre jeu. C'est toi qui vois.
En tout cas, voici un exemple pour faire tourner le jeu en externe (le fichier jar du jeu doit être mis dans le dossier de lancement de ton programme) (dans un IDE, pour tester, le dossier d'exécution, c'est le dossier du projet). J'ai simulé ton cas en faisant juste une fenêtre avec un togglebutton pour lancer/arrêter.
Le même avec deux boutons, un pour démarrer, un pour arrêter :
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 import java.awt.GridBagLayout; import java.io.IOException; import java.util.Arrays; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JToggleButton; public class Demo { private static final String FRAME_TITLE = "Lanceur PacMan"; private static final String START_LABEL = "Démarrer"; private static final String STOP_LABEL = "Arrêter"; private static Process process; public static void main(String[] args) { JFrame frame = new JFrame(FRAME_TITLE); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // ça c'est pour arrêter le pacman si on ferme la fenêtre du lanceur Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { @Override public void run() { if ( process!=null ) { process.destroy(); } } })); // la fenêtre affiche juste un bouton au milieu JPanel panel = new JPanel(new GridBagLayout()); frame.add(panel); // on utilise un toggle button pour lancer le jeu ou l'arrêter JToggleButton button = new JToggleButton(START_LABEL); button.addActionListener(e-> togglePacman(button)); panel.add(button); // on affiche la fenêtre du lanceur frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } private static void togglePacman(JToggleButton button) { if ( !button.isSelected() ) { // si le bouton a été cliqué alors qu'il était "enfoncé", alors on arrête le jeu process.destroy(); // on kill le process process=null; button.setText(START_LABEL); // on remet le texte pour redémarrer } else { // on lance le programme de jeu dans une autre jvm ProcessBuilder processBuilder = new ProcessBuilder(Arrays.asList("java.exe","-jar","JavaPacman.jar")).inheritIO(); try { process = processBuilder.start(); // on lance un thread qui va attendre que le programme se termine pour remettre le bouton correctement new Thread(new WaitForPacman(process,button)).start(); button.setText(STOP_LABEL); // on met le texte pour arrête le jeu } catch (IOException e) { e.printStackTrace(); JOptionPane.showMessageDialog(null, "Le jeu n'a pas démarré. Voir la console pour l'erreur."); button.setSelected(false); } } } private static class WaitForPacman implements Runnable { private Process process; private JToggleButton button; public WaitForPacman(Process process, JToggleButton button) { this.process=process; this.button=button; } @Override public void run() { try { process.waitFor(); // on attend que le jeu de pacman se termine } catch (InterruptedException e) { } // on remet le bouton pour redémarrer le jeu button.setText(START_LABEL); button.setSelected(false); process=null; } } }
En fait on nous demande de faire choisir un jeu et de le modifier ( le code, les images...)
Donc si tu dois modifier le code du jeu, alors fais-le.
Il y a deux principaux problèmes :
- Avoir le contrôle sur la fenêtre et le moteur du jeu pour pouvoir l'arrêter
- Pourvoir arrêter la boucle de jeu, rapidement et sans erreur (ou du moins, sans qu'elles se voient pour faire simple).
La boucle de jeu est définie dans la classe Display :
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 private class MainLoop implements Runnable { @Override public void run() { long desiredFrameRateTime = 1000 / 60; long currentTime = System.currentTimeMillis(); long lastTime = currentTime - desiredFrameRateTime; long unprocessedTime = 0; boolean needsRender = false; while (running) { currentTime = System.currentTimeMillis(); unprocessedTime += currentTime - lastTime; lastTime = currentTime; while (unprocessedTime >= desiredFrameRateTime) { unprocessedTime -= desiredFrameRateTime; update(); needsRender = true; } if (needsRender) { Graphics2D g = (Graphics2D) bs.getDrawGraphics(); g.setBackground(Color.BLACK); g.clearRect(0, 0, getWidth(), getHeight()); g.scale(game.screenScale.getX(), game.screenScale.getY()); draw(g); g.dispose(); bs.show(); needsRender = false; } else { try { Thread.sleep(1); } catch (InterruptedException ex) { } } } } }
- la première chose qu'on doit pouvoir faire, c'est arrêter la boucle while(running)Pour faire çà :
- il faut une méthode qu'on va ajouter à la classe
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 public void stop() { running=false; }- en multithread et en particulier avec plusieurs cores, le fait d'avoir besoin de modifier running dans un thread alors qu'il est testé dans un autre (celui de la boucle) pose un problème. La modification faite par un thread peut ne pas être vue par l'autre. Un moyen simple et rapide de résoudre le problème est de déclarer la variable volatile :
Code : Sélectionner tout - Visualiser dans une fenêtre à part private volatile boolean running- Un autre souci est qu'il y a une autre boucle à l'intérieur de la boucle while(running) et qu'il faut aussi qu'elle s'arrête quand running devient false.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 while (running && unprocessedTime >= desiredFrameRateTime) { unprocessedTime -= desiredFrameRateTime; update(); needsRender = true; }- on pourrait interrompre le thread (et donc traiter cette interruption dans le catch(InterruptedException) mais on va s'en passer et laisser mourir le thread tranquille.
seulement dans la boucle on manipule des composants graphiques qui ne seront plus utilisables lorsqu'on va arrêter le jeu. Il faut donc éviter de le faire, sinon on a une exception. Ce n'est pas très grave parce que ça plante pas le programme, mais on la voit dans la console, donc ce n'est pas très clean. Pour faire simple, on pourrait mettre un try/catch, mais on va utiliser un système qui permet d'intercepter toute exception dans le thread et de faire ce qu'on veut, comme simplement les ignorer pour juste laisser mourir le thread en silence
Dans la méthode start de Display, on a ajouter un UncaughtExceptionHandler :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9 Thread thread = new Thread(new MainLoop()); thread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { // ignore all } }); thread.start();- il est très important de libérer les ressources réservées pour le BufferStrategy
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 public void stop() { running=false; bs.dispose(); }- Ensuite, il faut gérer la fenêtre et l'instance de display, pour pouvoir l'arrêter. On va faire une classe de contrôle, et pour simplifier le contrôle, comme, à priori, on ne veut afficher qu'une seul instance de jeu à la fois, on va faire du static.
En résumé, il s'agit de :
- conserver une référence sur la JFrame pour pouvoir la fermer (appeler dispose())
- conserver une référence sur l'instance de Display pour pouvoir arrêter la boucle de jeu (appeler stop())
- remplacer le comportement de System.exit() lorsqu'on ferme le jeu, mais plutôt de libérer notre instance de contrôle, de fermer la fenêtre et d'arrêter la boucle de jeu
- d'ajouter un système de callback (le paramètre onStop) pour qu'on puisse gérer l'interface de lancement (pour l'adapter à l'arrêt du jeu par la fenêtre du jeu et non par le bouton de l'interface principale)
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 mport java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.JFrame; import test.br.ol.pacman.PacmanGame; import test.br.ol.pacman.infra.Display; public class PacMan { private static final String DEFAULT_NAME = "PacMan"; private static volatile PacMan pacman; private PacmanGame game; private Display view; private JFrame frame; private Runnable onStop; private PacMan(String name, Runnable onStop) { this.onStop=onStop; game = new PacmanGame(); view = new Display(game); frame = new JFrame(); setTitle(name); frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { stop(); } }); frame.getContentPane().add(view); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); view.requestFocus(); view.start(); } private void setTitle(String name) { if ( name==null ) { name=DEFAULT_NAME; } frame.setTitle(name); } private void shutdown() { view.stop(); frame.dispose(); if ( onStop!=null ) onStop.run(); } public static synchronized PacMan start(String name, Runnable onStop) { if ( pacman==null ) { pacman = new PacMan(name, onStop); } else { pacman.setTitle(name); } return pacman; } public static synchronized void setFrameTitle(String name) { if ( pacman!=null ) { pacman.setTitle(name); } } public static boolean isRunning() { return pacman!=null; } public static synchronized void stop() { if ( pacman!=null ) { pacman.shutdown(); pacman = null; } } }- Et pour la classe de démo/test, sur base de toggle button :
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 import java.awt.GridBagLayout; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JToggleButton; public class Demo { private static final String FRAME_TITLE = "Lanceur PacMan"; private static final String GAME_TITLE = "PacMan !!!"; private static final String START_LABEL = "Démarrer"; private static final String STOP_LABEL = "Arrêter"; public static void main(String[] args) { JFrame frame = new JFrame(FRAME_TITLE); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JPanel panel = new JPanel(new GridBagLayout()); frame.add(panel); JToggleButton button = new JToggleButton(START_LABEL); button.addActionListener(e-> togglePacman(button)); panel.add(button); frame.pack(); frame.setVisible(true); } private static void togglePacman(JToggleButton button) { if ( !button.isSelected() ) { PacMan.stop(); button.setText(START_LABEL); } else { button.setText(STOP_LABEL); PacMan.start(GAME_TITLE, ()->resetButton(button)); } } private static void resetButton(JToggleButton button) { button.setText(START_LABEL); button.setSelected(false); } }
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager