
IFT1020 - Automne 2004
TP2 - Jeu de Combat
 
But: pratiquer la création d'une programe complexe qui utilise héritage, les interfaces et l'animation
Date limite: 17 Novembre, 12h00 (midi)
Il faut créer un applet Java qui copie le vieux jeu vidéo Combat par Atari. Combat était le premier "tank game".
Étape 1
CréerTerrain.java
qui contient class
Terrain extends JPanel
et mettre un Terrain dans un class
TankApplet extends JApplet
Terrain est un paneau qui montre un terrain lu d'un fichier. Vous pouvez utiliser le fichier suivant:
Au dessus du terrain, vous devez imprimer les tanks. Vous pouvez utiliser tank_blue.gif et tank_red.gif ou vous pouvez créer vous-même un graphique.
Il doit être possible de redimensionner le terrain et les tanks. Donc votre Terrain devrait implanter l'interface ComponentListener
pour savoir quand la taille de l'applet est changée. Voir le code suivant pour un exemple (ResizableApplet.java).
L'exemple s'occupe du JPanel; nous allons fournir des details pour redimensionner les tanks plus tard dans un des démos.
Une nouvelle version qui utilise java.net.URL pour lire le fichier. Cette version va marcher même si on utilise
appletviewer http://www.iro.umontreal.ca/~pift1020/tp2/ResizableApplet.html. Aussi cette version utilise un MediaTracker pour
lire l'image.
import java.awt.*; import javax.swing.*; import java.awt.image.*; import java.awt.event.*; import java.net.URL; import java.net.MalformedURLException; public class ResizableApplet extends JApplet { public ResizablePanel p; private MediaTracker tracker=null; public void init() { Image combatImg = loadImageCompletely("combat.gif"); p = new ResizablePanel(combatImg); this.getContentPane().add(p); } private Image loadImageCompletely(String imgName) { //load image using URL. wait for load using MediaTracker Image img; if (tracker==null) { tracker = new MediaTracker(this); } try { img = Toolkit.getDefaultToolkit().getImage(new URL(this.getDocumentBase(),imgName)); tracker.addImage(img,0); try { tracker.waitForID(0); } catch (InterruptedException e) { System.out.println("Cannot load image "+ img); System.out.println(e); } return img; } catch (MalformedURLException e) { System.out.println(e); } return null; } } class ResizablePanel extends JPanel implements ComponentListener { private Image bImg=null; private Image bScaledImg=null; public ResizablePanel( Image bImg) { this.bImg = bImg; addComponentListener(this); } public void paintComponent(Graphics g) { super.paintComponent(g); if (bScaledImg==null) { resizeTerrainImage(); } g.drawImage(bScaledImg,0,0,this); } public void componentHidden(ComponentEvent e) {} public void componentMoved(ComponentEvent e) {} public void componentShown(ComponentEvent e) {} public void componentResized(ComponentEvent e) { resizeTerrainImage(); } private void resizeTerrainImage() { bScaledImg = bImg.getScaledInstance(this.getSize().width, this.getSize().height,Image.SCALE_DEFAULT); } }La vielle version:
import java.awt.*; import javax.swing.*; import java.awt.image.*; import java.awt.event.*; public class ResizableApplet extends JApplet { public ResizablePanel p; public void init() { p = new ResizablePanel("combat.gif"); this.getContentPane().add(p); } } class ResizablePanel extends JPanel implements ComponentListener { private Image bImg=null; private Image bScaledImg=null; public ResizablePanel( String bImageName) { bImg = Toolkit.getDefaultToolkit().getImage(bImageName); addComponentListener(this); } public void paintComponent(Graphics g) { super.paintComponent(g); if (bScaledImg==null) { resizeTerrainImage(); } g.drawImage(bScaledImg,0,0,this); } public void componentHidden(ComponentEvent e) {} public void componentMoved(ComponentEvent e) {} public void componentShown(ComponentEvent e) {} public void componentResized(ComponentEvent e) { resizeTerrainImage(); } private void resizeTerrainImage() { bScaledImg = bImg.getScaledInstance(this.getSize().width, this.getSize().height,Image.SCALE_DEFAULT); } }
Étape 2
Implanter abstract class Tank qui contient:public Tank(String tankName, Image image, TerrainInterface terrain); public String toString(); public Image getImage(); public void start();TankInterface est un interface pour Les classes qui heritent de Tank peuvent utiliser le TerrainInterface.java pour naviguer dans le jeu. Dans étape 2 vous pouvez avoir un implementation de TerrainInterface dans Terrain très simple, par exemple:
public TankList getTankList() { return (TankList)null; } public boolean canTankMakeMove(Tank t, int dir) { return false; } public double rotateDelta() { return 0;} public double moveDelta() { return 0;}Dans étape 9 vous pouvez implementer les "vrai" méthodes.
Implanter class RandomTank extends Tank
. RandomTank doit utilser un Timer pour se déplacer au hasard dans le jeu.
Pour commmuniquer avec le jeu, le RandomTank doit generer les TankEvents
. Voir en bas.
Étape 3
Implanterclass Terrain
qui accepte TankEvents
des Tanks
en utilisant TankEvent
(TankEvent.java):
import java.util.EventObject; class TankEvent extends EventObject { private int move=0; private int rotate=0; private boolean fire=false; TankEvent(Object source, int move, int rotate, boolean fire) { super(source); this.move=move; this.rotate=rotate; this.fire=fire; } public int getMove() { return move; } public int getRotate() { return rotate; } public boolean getFire() { return fire; } public String toString() { return "Move=" + move + " rotate=" + rotate + "fire=" + fire; } }Ajouter une méthode
addTankListener(TankListener tl)
dans class Tank
.
Puis, quand vous créez votre Terrain et Tanks, vous pouvez failre:
Terrain terrain = new Terrain(); Tank t = new RandomTank("redTank",redImg, terrain); t.addTankListener(terrain);Maintentant vous devriez instantier deux RandomTanks dans votre Terrain et les voir se deplacer au hasard dans le jeu.
Étape 4
Implanter les obstacles pour que les tanks ne peuvent pas passer par des murs, obstacles etc. Norman va vous fournir une stratégie pour ça dans un des démos.
Voir l'exemple suivant (ObstacleApplet.java et ObstacleApplet.html):
import java.awt.*; import javax.swing.*; import java.awt.image.*; import java.awt.event.*; import java.net.URL; import java.net.MalformedURLException; public class ObstacleApplet extends JApplet { public ObstaclePanel p; private MediaTracker tracker=null; public void init() { Image combatImg = loadImageCompletely("combat.gif"); p = new ObstaclePanel(combatImg); this.getContentPane().add(p); } private Image loadImageCompletely(String imgName) { //load image using URL. wait for load using MediaTracker Image img; if (tracker==null) { tracker = new MediaTracker(this); } try { img = Toolkit.getDefaultToolkit().getImage(new URL(this.getDocumentBase(),imgName)); tracker.addImage(img,0); try { tracker.waitForID(0); } catch (InterruptedException e) { System.out.println("Cannot load image "+ img); System.out.println(e); } return img; } catch (MalformedURLException e) { System.out.println(e); } return null; } } class ObstaclePanel extends JPanel implements ComponentListener, MouseListener { private Image bImg=null; private BufferedImage bScaledBufferedImg=null; public ObstaclePanel( Image bImg) { this.bImg = bImg; addComponentListener(this); addMouseListener(this); } public void paintComponent(Graphics g) { super.paintComponent(g); if (bScaledBufferedImg==null) { resizeTerrainImage(); } g.drawImage(bScaledBufferedImg,0,0,this); } public void componentHidden(ComponentEvent e) {} public void componentMoved(ComponentEvent e) {} public void componentShown(ComponentEvent e) {} public void componentResized(ComponentEvent e) { resizeTerrainImage(); } private void resizeTerrainImage() { int w = this.getSize().width; int h = this.getSize().height; bScaledBufferedImg = (BufferedImage)this.createImage(w,h); if ( bScaledBufferedImg == null ) { return; } Graphics2D g = bScaledBufferedImg.createGraphics(); g.drawImage(bImg, 0, 0, w, h, this); g.dispose(); } public void mouseClicked(MouseEvent e) { int obsRGBColor = -8041230; int pixel = bScaledBufferedImg.getRGB(e.getX(),e.getY()); System.out.print("Color="+pixel); if (pixel==obsRGBColor) { System.out.println(" obstacle!"); } else { System.out.println(" OK"); } } public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} public void mousePressed(MouseEvent e) {} public void mouseReleased(MouseEvent e) {} }
Étape 5
Implanterclass UserTank extends Tank
qui utilse le clavier pour controller le tank. Utilser les flèches pour naviguer et le
barre d'espacement pour tirer.
Voir l'exemple suivant (KeyTrackApplet.java et KeyTrackApplet.html):
import java.awt.*; import java.awt.event.*; import javax.swing.*; public class KeyTrackApplet extends JApplet implements KeyEventDispatcher{ int vPress=0; public void init() { setFocusable(true); } public void paint(Graphics g) { super.paint(g); if (vPress==1) { g.drawString("Tracking key v... pressed",10,20); } else if (vPress==-1) { g.drawString("Tracking key v... released",10,20); } else { g.drawString("Try pressing key v",10,20); } } public void start() { KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(this); } public boolean dispatchKeyEvent(KeyEvent e) { // Nous traitons seulement VK_V ("v"). // Voir documentation pour KeyEventDispatcher et KeyEvent (par exemple "google KeyEventDispatcher") // pour voir tous les "keycodes" (comme KEY_PRESSED et VK_V) if (e.getKeyCode()==KeyEvent.VK_V) { if (e.getID() == KeyEvent.KEY_PRESSED) vPress=1; else vPress=-1; repaint(); } return false; } }
Maintentant vous devriez instantier un RandomTank et un UserTank dans votre Terrain et voir le RandomTank se deplacer au hasard en même temps que vous pilotez le UserTank.
Étape 6
Implanter les balles. Chaque tank peut avoir seulement une balle à la fois. Donc si un tank tire en même temps que une balle est déjà lancée, la première balle doit disparaître. Les balles ne peuvent pas passer par les murs et les obstacles.Étape 7
Implanter l'habilité pour les tanks d'être atteint par des balles. Il faut compter les atteintes. Quand un tank est touché 5 fois, il est "mort". Le tank devrait disparaître et réapparaître dans une place choisi au hasard, mais loin des autres tanks et pas à l'interieur "un obstacle.Étape 8
Monter le pointage. Le jeu est fin quand un tank arrive à 10 points (10 tués). Ce n'est pas nécessaire de copier les chiffres de l'interface de Atari 2600.Étape 9
Implanterclass SmartTank extends RandomTank
qui utilise votre meilleur algorithme pour gagner.
Notre implanatation
Vous pouvez voir un exemple en faisant les commandes suivantes. Il est normal que vous ne puissiez lire le contenu de ce répertoire mais vous pouvez accéder quand même au programme en tapant ces commandes telles quelles (dans une machine linux):
appletviewer http://www.iro.umontreal.ca/~pift1020/tp2/Tank.html
Détail de soumission
Il faut ensuite envoyer, par la procédure de remise électronique habituelle, les fichiers nécessaires à l'éxécution de votre programme avant le vendredi 17 novembre 2004 à 12:00 (midi).
Vous remettrez également une copie papier un court rapport et votre programme Java lors de la séance du cours du 1 octobre.
Ce TP devra être fait seul. Vous pouvez aider vos collègues avec les questions générales de java, mais vous ne pouvez pas fournir le code.
TP1 sera noté sur 30:
étape 1 | 3 |
étape 2 | 2 |
étape 3 | 2 |
étape 4 | 2 |
étape 5 | 2 |
étape 6 | 2 |
étape 7 | 2 |
étape 8 | 1 |
étape 9 | 4 |
résultats corrects | 3 |
interface usager (GUI) | 3 |
lisibilité du code | 2 |
rapport | 2 |
total | 30 |
Bonne chance!
Doug Eck