import java.awt.Color; import java.awt.Graphics; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.image.BufferedImage; import java.util.ArrayList; import javax.swing.JApplet; /* * @author Gaël Cuenot * */ public class Invaders extends JApplet implements KeyListener { /* Variables generales */ // taille de la fenetre private static final int WIN_WIDTH = 500; private static final int WIN_HEIGHT = 300; // les buffers pour l'affichage à l'ecran private BufferedImage backBuffer; private Graphics backBufferGraphics; public int score = 0; public int niveau = 1; public int vies = 5; /* Messages */ private String affMonstres = "Nombre de monstres: "; private String affVies = "Vies: "; private String affNiveau = "Niveau: "; private String affScore = "Score: "; /* Variables pour la gestion du vaisseau */ public int posX, posY; // variables permettant de donner la direction du vaisseau private boolean gauche = false; private boolean droite = false; public Vaisseau ship; // le vaisseau /* Variables pour les monstres */ // pas de deplacement vertical des monstres private static final int VERTICAL_SPEED = 5; /* pas de deplacement horizontal des monstres, deplacement_prec est utilise * pour detecter un changement de sens */ public int deplacement = 1; public int deplacement_prec; // liste contenant les monstres public static ArrayList lsMonstres = null; // nombre de monstres et leur pas de deplacement horizontale public int nbr_monstres = 27; public int m_vitesse = 1; public Monstre premierMonstre; public Monstre dernierMonstre; /* Variables pour les tirs */ private boolean tir = false; private static ArrayList lsTirs = null; private long dernierTir = 0; private long intervalTir = 300; /* Variables pour la gestion du jeu */ private boolean enterKeyPressed = false; private boolean escKeyPressed = false; public boolean gameOver = false; private boolean gameWin = false; private boolean gameLost = false; private boolean gameNew = true; /* Methode d'initialisation */ public void init() { System.out.println("Bonjour, je m'initialise"); // definition de la taille de la fenetre et de sa couleur de fond this.setSize(WIN_WIDTH, WIN_HEIGHT); setBackground(Color.black); addKeyListener(this); requestFocus(); // chargement des images en cache SpriteCache s = new SpriteCache(); s.loadSprites(); // creation et initialisation du double buffer backBuffer = new BufferedImage(this.getWidth(),this.getHeight(),BufferedImage.TYPE_INT_RGB); backBufferGraphics = backBuffer.getGraphics(); // creation du vaisseau en bas de la fenetre, au milieu posX = (WIN_WIDTH / 2) - 17; // 17 correspond a la moitier de la largeur de l'image posY = WIN_HEIGHT - 40; ship = new Vaisseau(posX, posY, vies); // creation du groupe de monstres et initialisation de la liste des tirs initMonstres(); lsTirs = new ArrayList(); } // methode d'initialisation du groupe de monstres public void initMonstres() { // position initiale du 1er monstre int m_posX = 1; int m_posY = 20; lsMonstres = new ArrayList(); /* boucle de creation de la liste de 27 monstres (3x9 monstres) * chaque monstre est a une distance de 40px d'un autre */ for (int i = 0; i < nbr_monstres; i++) { m_posX += 40; if (i % 9 == 0) { m_posY += 40; m_posX = 1; } lsMonstres.add(new Monstre(m_posX, m_posY, m_vitesse)); } } /* methode de deplacement du groupe de monstres * deplacement correspond a la distance d'un deplacement horizontal de chaque monstre * dep_v correspond a la distance d'un deplacement vertical de chaque monstre */ public void deplaceMonstres(int deplacement, int dep_v) { for (int j = 0; j < lsMonstres.size(); j++) ((Monstre) lsMonstres.get(j)).deplacement(deplacement, dep_v); } // methode qui dessine le groupe de monstres public void dessineMonstres() { for (int i = 0; i < lsMonstres.size(); i++) ((Monstre) lsMonstres.get(i)).dessiner(backBufferGraphics, this); } // methode qui deplace les tirs du vaisseau public void deplaceTirs() { for (int j = 0; j < lsTirs.size(); j++) ((Tir) lsTirs.get(j)).deplacement(); } // methode de suppression d'un tir de la liste public static void effaceTir(Tir t) { lsTirs.remove(t); } // methode qui dessine les tirs du vaisseau public void dessineTirs() { for (int i = 0; i < lsTirs.size(); i++) ((Tir) lsTirs.get(i)).dessiner(backBufferGraphics, this); } /* methode qui valide un tir, c'est a dire que le vaisseau tire seulement * si le delais minimum entre 2 tirs est ecoule */ public void essayeTir() { // verification que le temps entre 2 tirs est suffisant if (System.currentTimeMillis() - dernierTir < intervalTir) return; /* memorisation de l'instant correspondant au tir actuel * et creation du tir */ dernierTir = System.currentTimeMillis(); Tir t = new Tir(ship.getPos().x + 11, ship.getPos().y - 30); lsTirs.add(t); } // methode de deplacement du vaisseau public void deplaceVaisseau() { if (gauche) ship.deplacement(-1); if (droite) ship.deplacement(1); if (tir) essayeTir(); } // methode qui defini la vitesse de deplacement des monstres en fonction du niveau courant public void monstresVitesse() { if (niveau % 2 == 0 && niveau > 1) m_vitesse++; } // methode de gestion des collisions public void collisions() { // parcours de toute la liste contenant les tirs for (int i = 0; i < lsTirs.size(); i++) { Tir curTir = (Tir) lsTirs.get(i); // stockage du tir courant dans curTir // parcours de toute la liste des monstres for (int j = 0; j < lsMonstres.size(); j++) { Monstre curM = (Monstre) lsMonstres.get(j); // stockage du monstre courant dans curTir /* 2 boucles inbriquees pour parcourir chaque pixel constituant * l'image du tir courant (curTir). * les images ayant des bords noir, ces marges sont en parties rognees */ for (int tir_x = curTir.pos.x; tir_x < curTir.pos.x + 12; tir_x++) { for (int tir_y = curTir.pos.y; tir_y < curTir.pos.y + 23; tir_y++) { /* pour chaque pixel du tir, verification qu'il n'est pas commun * avec la position du monstre courant (curM). */ if ((tir_x > curM.pos.x + 5 && tir_x < curM.pos.x + 25) && (tir_y > curM.pos.y + 5 && tir_y < curM.pos.y + 20)) { score += niveau; // incrementation du score en fonction du niveau lsMonstres.remove(curM); // suppression du monstre touche lsTirs.remove(curTir); //suppression du tir responsable de la collision } } } } } } /* methode permettant de definir a caque instant la position du monstre de la liste * situe le plus a gauche, et celui situe le plus a droite. * le deplacement du groupe de monstre est base sur les variables * premierMonstre et dernierMonstre modifiees dans cette methode */ public void setPremierEtDernier() { /* initialisation des variables premierMonstre et dernierMonstre * avec la position du premier monstre de la liste */ premierMonstre = (Monstre) lsMonstres.get(0); dernierMonstre = premierMonstre; // parcours de la liste de monstres pour determiner le + a droite et le + a gauche for (int i = 0; i < lsMonstres.size(); i++) { if (((Monstre) lsMonstres.get(i)).pos.x < premierMonstre.pos.x) premierMonstre = (Monstre) lsMonstres.get(i); if (((Monstre) lsMonstres.get(i)).pos.x > dernierMonstre.pos.x) dernierMonstre = (Monstre) lsMonstres.get(i); } } // methode d'affichage des informations affichees a l'ecran durant le jeu public void AffMessages(Graphics g) { affNiveau = "Niveau: " + niveau; affScore = "Score: " + score; affMonstres = "Monstres: " + lsMonstres.size(); affVies = "Vies: " + ship.vie; g.setColor(Color.white); backBufferGraphics.drawString(affNiveau, 5, 15); backBufferGraphics.drawString(affScore, 5, 30); backBufferGraphics.drawString(affMonstres, WIN_WIDTH - 80, 15); backBufferGraphics.drawString(affVies, WIN_WIDTH - 80, 30); } public void keyTyped(KeyEvent e) { ; } /* methode de gestion des evenements lorsque certaines touches * sont relachees */ public void keyReleased(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_LEFT: gauche = false; break; case KeyEvent.VK_RIGHT: droite = false; break; case KeyEvent.VK_SPACE: tir = false; break; case KeyEvent.VK_ENTER: enterKeyPressed = false; break; case KeyEvent.VK_ESCAPE: escKeyPressed = false; break; } } /* methode de gestion des evenements lorsque certaines touches * sont pressees */ public void keyPressed(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_LEFT: gauche = true; break; case KeyEvent.VK_RIGHT: droite = true; break; case KeyEvent.VK_SPACE: tir = true; break; case KeyEvent.VK_ENTER: enterKeyPressed = true; break; case KeyEvent.VK_ESCAPE: escKeyPressed = true; break; } } // methode paint gerant l'affichage de la fenetre public void paint(Graphics g) { int dep_v = 0; /* reinitialisation de dep_v a 0 pour que les monstres * ne se deplacent verticalement qu'une seul fois */ if (escKeyPressed) destroy(); // l'applet est stoppee // on commence une nouvelle partie if (gameNew) { backBufferGraphics.drawString("Nouvelle partie, appuyer sur Enter pour commencer",(WIN_WIDTH-g.getFontMetrics().stringWidth("Nouvelle partie, appuyer sur Enter pour commencer"))/2,WIN_HEIGHT/2); g.drawImage(backBuffer, 0, 0, this); requestFocus(); repaint(); if (enterKeyPressed) { gameLost = false; gameWin = false; gameOver = false; gameNew = false; init(); } } // fin d'une partie else if (gameOver) { // dans le cas ou le joueur a perdu if (gameLost) { // si il reste assez de vies, on recommence le niveau if (vies > 1) { backBufferGraphics.drawString("Touche !! Appuyer sur Enter pour reessayer (Esc pour quitter)",(WIN_WIDTH - g.getFontMetrics().stringWidth("Perdu !! Appuyer sur Enter pour reessayer (Esc pour quitter)"))/2,WIN_HEIGHT/2); g.drawImage(backBuffer, 0, 0, this); requestFocus(); repaint(); if (enterKeyPressed) { gameLost = false; gameWin = false; gameOver = false; gameNew = false; vies--; init(); } } // sinon c'est perdu, reinitialisation du jeu else { backBufferGraphics.drawString("Perdu !! Appuyer sur Enter pour recommencer (Esc pour quitter)",(WIN_WIDTH - g.getFontMetrics().stringWidth("Perdu !! Appuyer sur Enter pour recomencer (Esc pour quitter)"))/2,WIN_HEIGHT/2); g.drawImage(backBuffer, 0, 0, this); requestFocus(); repaint(); if (enterKeyPressed) { gameLost = false; gameWin = false; gameOver = false; gameNew = true; niveau = 1; vies = 5; m_vitesse = 1; init(); } } } // cas d'une partie gagnee if (gameWin) { backBufferGraphics.drawString("Gagne !! Appuyer sur Enter pour le niveau suivant (Esc pour quitter)",(WIN_WIDTH - g.getFontMetrics().stringWidth("Perdu !! Appuyer sur Enter pour le niveau suivant (Esc pour quitter)"))/2,WIN_HEIGHT/2); g.drawImage(backBuffer, 0, 0, this); requestFocus(); repaint(); if (enterKeyPressed) { gameWin = false; gameLost = false; gameOver = false; niveau++; monstresVitesse(); init(); } } } // un niveau est en fonctionnement else { /* ceci permet au jeu de se derouler a la meme vitesse quelle que soit * la puissance de la machine qui execute le code */ try { Thread.sleep(10); } catch (Exception e) { } // initialisation des variables dernierMonstre et premierMonstre setPremierEtDernier(); /* si le monstre le + a droite a atteint le bord droit de la fenetre * le groupe change de sens et descend d'un cran */ if (dernierMonstre.pos.x >= getWidth() - 30) { deplacement = -1; dep_v = VERTICAL_SPEED; } /* si le premier monstre le + a gauche a atteint le bord gauche de la fenetre * le groupe change de sens et descend d'un cran */ if (premierMonstre.pos.x <= 0) { deplacement = 1; dep_v = VERTICAL_SPEED; } // effacement de tout ce qui est a l'ecran backBufferGraphics.clearRect(0, 0, getWidth(), getHeight()); // on donne le focus aux peripherique d'entree (clavier dans ce cas) requestFocus(); // deplacement et affichage des monstres deplaceMonstres(deplacement, dep_v); dessineMonstres(); dep_v = 0; // deplacement et affichage du vaisseau deplaceVaisseau(); ship.dessiner(backBufferGraphics, this); // deplacement et affichage du vaisseau deplaceTirs(); dessineTirs(); // verification des collisions collisions(); // affichage des informations a l'ecran AffMessages(backBufferGraphics); // s'il ne reste plus de monstre la partie est finie et le joueur a gagne if (lsMonstres.size() == 0) { gameWin = true; gameOver = true; } // si au moins un monstre arrive a la hauteur du vaisseau la partie est finie et le joueur a perdu else if ((((Monstre) (lsMonstres.get(lsMonstres.size() - 1))).getPos().y >= getHeight() - 65)) { gameLost = true; gameOver = true; } } repaint(); // dessin des images contenues dans le buffer g.drawImage(backBuffer, 0, 0, this); } /* * methode qui retourne la hauteur de la fenetre (utilise dans la classe vaisseau) */ public static int getWIN_HEIGHT() { return WIN_HEIGHT; } /* * methode qui retourne la largeur de la fenetre (utilise dans la classe vaisseau) */ public static int getWIN_WIDTH() { return WIN_WIDTH; } }