Etape 11 : Détection des Collisions
Il s’agit de la dernière étape. Le jeu a actuellement toutes les fonctionnalités avec mouvement de tuyau, le mouvement des oiseaux, etc.. Maintenant, nous allons écrire le code qui détermine quand le joueur a perdu au sein de TopClass. Nous accomplirons ceci avec une seule ligne de code dans la boucle de jeu et les deux méthodes (collisionDetection et collisionHelper). Ci-dessous vous voyez une vidéo de quelques tests de collision.
Avant tout, créer le squelette de collisionDetection (donc eclipse ne reçoit pas en colère contre vous), puis ajoutez "collisionDetection (bp1, bp2, tp1, tp2, oiseau);", juste avant la méthode updateScore est appelée dans la boucle de jeu.
Si vous pensez à ce sujet, il y a cinq collisions possibles qui peuvent se produire. L’oiseau pourrait entrer en collision avec l’un des quatre tuyaux ni le sol. Cela signifie que vous pouvez créer une méthode d’assistance qui gère la logique identique pour les quatre collisions de conduite possible. Pour cette raison, nous allons commencer avec la méthode collisionHelper.
Logiquement, c’est comment nous allons afin de détecter une collision (quelque chose que j’ai développé, donc je ne sais pas comment efficace il est comparé à d’autres méthodes de détection de collision) :
* Nous devons les classes joueur et obstacle pour avoir des méthodes qui retournent un objet Rectangle et l’objet BufferedImage
* À l’aide de Rectangles, nous vérifions si Rectangle l’oiseau croise Rectangle d’une conduite particulière
* Si il y a une intersection, obtenir la gamme des coordonnées qui lié à l’intersection (premier x, x final, premier y, y final)
* À l’aide de l’oiseau et tuyau BufferedImages, tester chaque pixel dans la zone de collision pour chaque BufferedImage ; Si le pixel n’est pas transparent (n’oubliez pas que les graphismes sont entrelacés) pour les oiseaux et tuyau, puis une collision s’est produite
Sachant cela, on commence à travailler sur collisionHelper. Rectangles sont gentils parce que vous pouvez tester si les intersections ont eu lieu, mais aussient garder une trace de position de l’écran de la zone d’intersection.
Nous créons un nouveau rectangle qui est l’intersection entre l’oiseau et de tuyau. La variable firstI est le premier X pixel pour effectuer une itération ; C’est la différence entre le côté gauche du rectangle intersection et la coordonnée X de l’oiseau (r1) loin à gauche. La variable firstJ est le premier pixel Y parcourir C’est la différence entre le côté supérieur du rectangle intersection et haut Y coordonnée l’oiseau. Ces deux sont utilisés pour le référencement de l’objet de l’oiseau.
Nous devons également variables d’assistance à utiliser lorsque vous référencez l’objet de la collision. Les variables bp1XHelper et bp1YHelper utilisent une logique semblable comme firstI et firstJ, sauf qu’ils référencent les objets de l’oiseau et de collision.
Pour notre analyse itérative, nous créons une boucle imbriquée dans une autre boucle, une itération de la firstI/J à la largeur/hauteur de l’objet de l’oiseau (r.getWidth() + firstI est identique à r1.getWidth()). Intégré au sein de la boucle interne, nous avons une instruction conditionnelle qui teste la transparence du pixel. B1.getRGB (i, j) & 0xFF000000 simplement saisit la valeur alpha du pixel et si elle n’a pas la valeur de 0 x 00, il y a un pixel opaque présent. Nous vérifions les b1 et b2, et si les deux sont opaques, il y a eu une collision.
Si une collision se produit, nous envoyer « Game Over » à PlayGameScreen pour la peinture à travers l’écran, mettre fin à la boucle de jeu en changeant loopVar false, indiquer que le jeu s’est terminée en changeant de gamePlay sur false et rompre avec la boucle.
------
Maintenant, il est assez simple d’étoffer les collisionDetection. Commencez en appelant collisionHelper pour l’oiseau et chacun des objets de tuyauterie, en passant leur Rectangle relative et objets BufferedImage. Ensuite, vérifier si il y a eu une collision avec le « sol » en testant si le fond de l’oiseau a dépassé la position de la terre (SCREEN_HEIGHT * 7/8). Si oui, envoyez « Game Over » mettre fin à la boucle de jeu et indiquer le jeu s’est terminée.
java.awt.Dimension d’importation ; import java.awt.Font ; import java.awt.Image ; import java.awt.Color ; importation impossible ; java.awt.Rectangle d’importation ; java.awt.Toolkit importation ; import java.awt.event.ActionEvent ; java.awt.event.ActionListener d’importation ; import java.awt.event.KeyEvent ; java.awt.event.KeyListener d’importation ; java.awt.image.BufferedImage d’importation ; Import javax.swing.* ; TopClass/public class implements ActionListener, KeyListener {//global variables constantes privé public static final int SCREEN_WIDTH = (int) Toolkit.getDefaultToolkit().getScreenSize().getWidth() ; privé public static final int SCREEN_HEIGHT = (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight() ; privé public static final int PIPE_GAP = SCREEN_HEIGHT/5 ; //distance en pixels entre les tuyaux privé public static final int PIPE_WIDTH = SCREEN_WIDTH/8, PIPE_HEIGHT = 4 * PIPE_WIDTH ; privé public static final int BIRD_WIDTH = 120 , BIRD_HEIGHT = 75 ; privé public static final int UPDATE_DIFFERENCE = 25 ; temps en ms entre les mises à jour privé public static final int X_MOVEMENT_DIFFERENCE = 5 ; le passage de tuyaux à distance chaque mise à jour privé public static final int SCREEN_DELAY = 300 ; nécessaire en raison du temps forçant les tuyaux de chargement long pop up Mid-écran privé public static final int BIRD_X_LOCATION = SCREEN_WIDTH/7 ; privé public static final int BIRD_JUMP_DIFF = 10, BIRD_FALL_DIFF = BIRD_JUMP_DIFF/2, BIRD_JUMP_HEIGHT = PIPE_GAP - BIRD_HEIGHT - BIRD_JUMP_DIFF * 2 ; variables globales privées booléenne loopVar = true ; faux -> ne pas exécuter la boucle ; vrai -> exécution boucle pour pipes privée gamePlay booléen = false ; faux -> match ne pas joué privé boolean birdThrust = false ; faux -> touche pas à déplacer le birdFired booléenne verticalement privé oiseau = false ; vrai -> touche saut prématurément privé boolean sorti = true ; barre d’espace libéré ; commence comme vrai alors première presse enregistre private int birdYTracker = SCREEN_HEIGHT/2 - BIRD_HEIGHT ; Private Object buildComplete = new Object() ; swing global des objets privés JFrame f = new JFrame ("Flappy oiseau Redux") ; Private JButton startGame ; privé JPanel Top ; déclarés globalement pour accueillir l’opération repeints et permettre removeAll(), etc. //other global objets privés statique TopClass tc = new TopClass() ; private static PlayGameScreen pgs ; panneau qui possède l’expérience mobile au début du jeu / ** * constructeur par défaut * / public TopClass() {} / ** * méthode exécutable principal invoqué lorsque vous exécutez le fichier .jar * args * / public public static void main (String [] args) {//build le GUI sur un nouveau javax.swing.SwingUtilities.invokeLater de fil (nouvelle Runnable() {public void run() {tc.buildFrame() ; //create un nouveau thread pour garder l’interface graphique sensible tandis que le jeu déroule Thread t = new Thread() {public void run() {tc.gameScreen(true);}}; t.start();}});} / ** * méthode pour construire la JFrame et ajouter le contenu du programme * / private Sub buildFrame() {Image icône = Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("resources/blue_bird.png")) ; f.setContentPane(createContentPane()) ; f.setResizable(true) ; f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) ; f.setAlwaysOnTop(false) ; f.setVisible(true) ; f.setMinimumSize (Nouvelle Dimension (SCREEN_WIDTH * SCREEN_HEIGHT 1/4, * 1/4)); f.setExtendedState(JFrame.MAXIMIZED_BOTH) ; f.setIconImage(icon) ; f.addKeyListener(this);} privé JPanel createContentPane() {Top = new JPanel() ; //top-most JPanel dans la layout hiérarchie topPanel.setBackground(Color.BLACK) ; //allow nous les panneaux LayoutManager superposition de couche = new OverlayLayout(topPanel) ; topPanel.setLayout(overlay) ; //Start jeu JButton startGame = new JButton ("Start Playing!"); startGame.setBackground(Color.BLUE) ; startGame.setForeground(Color.WHITE) ; startGame.setFocusable(false) ; plutôt que de simplement setFocusabled(false) startGame.setFont (new Font ("Calibri", Font.BOLD, 42)) ; startGame.setAlignmentX(0.5f) ; Centrer horizontalement sur l’écran startGame.setAlignmentY(0.5f) ; centrer verticalement sur l’écran startGame.addActionListener(this) ; topPanel.add(startGame) ; Ajouter en dernier afin d’assurer des pgs de visibilité du bouton = new PlayGameScreen (vrai SCREEN_WIDTH, SCREEN_HEIGHT,) ; vrai--> nous voulons pgs pour le splash screen topPanel.add(pgs) ; retour Top ; } / ** * Mise en œuvre des événements d’action * / public void actionPerformed (ActionEvent e) {if(e.getSource() == startGame) {//stop le splash screen loopVar = false ; fadeOperation();} if(e.getSource() d’autre == buildComplete) {Thread t = Thread() nouveau {public void run() {loopVar = true ; gamePlay = true ; tc.gameScreen(false);}}; t.start();}} public void keyPressed (KeyEvent e) {if(e.getKeyCode() == KeyEvent.VK_SPACE & & gamePlay == true & & libéré == true) {//update une valeur booléenne qui est testée dans la boucle de jeu pour déplacer l’oiseau if(birdThrust) {//need cela s’inscrire la presse bouton et réinitialiser le birdYTracker avant l’opération de saut est terminée birdFired = true ; } birdThrust = true ; sortie = false ; } if(e.getKeyCode() d’autre == KeyEvent.VK_B & & gamePlay == false) {birdYTracker = SCREEN_HEIGHT/2 - BIRD_HEIGHT ; birdThrust hauteur de départ de //need pour réinitialiser l’oiseau = false ; //if utilisateur appuie sur espace avant la collision et une collision se produit avant d’atteindre la hauteur max, vous obtenez saut résiduelle, donc il s’agit de prévention actionPerformed (ActionEvent nouveau (startGame, -1, ""));} if(e.getKeyCode() == KeyEvent.VK_ESCAPE) {System.exit(0);}} public void keyReleased (KeyEvent e) {if(e.getKeyCode() == KeyEvent.VK_SPACE) {sorti = true;}} {} public void keyTyped (KeyEvent e) / ** * effectuer l’opération de fondu qui se déroulent avant le départ de tours * / private void fadeOperation() {Thread t = Thread() nouveau {public void run() {topPanel.remove(startGame) ; topPanel.remove(pgs) ; topPanel.revalidate() ; topPanel.repaint() ; //panel se faner JPanel temp = new JPanel() ; int alpha = 0; //alpha voie variable temp.setBackground (nouvelle couleur (0 0, 0, alpha)) ; topPanel.add(temp) de JPanel transparent, noir ; topPanel.add(pgs) ; topPanel.revalidate() ; topPanel.repaint() ; currentTime long = System.currentTimeMillis() ; while(temp.getBackground().getAlpha()! = 255) {if((System.currentTimeMillis() - currentTime) > UPDATE_DIFFERENCE/2) {si (alpha < 255-10) {alpha += 10;} else {alpha = 255;} temp.setBackground (nouvelle couleur (0, 0, 0, alpha)); topPanel.revalidate() ; topPanel.repaint() ; currentTime = System.currentTimeMillis();}} topPanel.removeAll() ; topPanel.add(temp) ; PGS = new PlayGameScreen (faux SCREEN_WIDTH, SCREEN_HEIGHT,) ; pgs.sendText("") ; supprimer le titre texte topPanel.add(pgs) ; while(temp.getBackground().getAlpha()! = 0) {if((System.currentTimeMillis() - currentTime) > UPDATE_DIFFERENCE/2) {if(alpha > 10) {alpha-= 10;} else {alpha = 0;} temp.setBackground (nouvelle couleur (0, 0, 0, alpha)); topPanel.revalidate() ; topPanel.repaint() ; currentTime = System.currentTimeMillis();}} actionPerformed (ActionEvent nouveau (buildComplete, -1, "Build terminée")) ; } }; t.Start() ; } / ** * Méthode qui exécute les mouvements graphiques de splash screen * / private void gameScreen (boolean isSplash) {BottomPipe bp1 = new BottomPipe (PIPE_WIDTH, PIPE_HEIGHT) ; Bp2 BottomPipe = new BottomPipe (PIPE_WIDTH, PIPE_HEIGHT) ; Tp1 TopPipe = new TopPipe (PIPE_WIDTH, PIPE_HEIGHT) ; TopPipe tp2 = new TopPipe (PIPE_WIDTH, PIPE_HEIGHT) ; Des oiseaux oiseaux = nouvel oiseau (BIRD_WIDTH, BIRD_HEIGHT) ; variables pour suivre les x et y image emplacements pour le bas tube int xLoc1 = SCREEN_WIDTH + SCREEN_DELAY, xLoc2 = (int) ((double) 3.0/2.0*SCREEN_WIDTH+PIPE_WIDTH/2.0)+SCREEN_DELAY ; int yLoc1 = bottomPipeLoc(), yLoc2 = bottomPipeLoc() ; int birdX = BIRD_X_LOCATION, birdY = birdYTracker ; variable destinée à contenir le début de la boucle du temps long startTime = System.currentTimeMillis() ; while(loopVar) {if((System.currentTimeMillis() - startTime) > UPDATE_DIFFERENCE) {//check si un ensemble de tuyaux a laissé l’écran //if, réinitialiser le tuyau X emplacement et assigner un nouvel emplacement Y si (xLoc1 < (0-PIPE_WIDTH)) {xLoc1 = SCREEN_WIDTH ; yLoc1 = bottomPipeLoc();} ElseIf (xLoc2 < (0-PIPE_WIDTH)) {xLoc2 = SCREEN_WIDTH ; yLoc2 = bottomPipeLoc();} //decrement les emplacements de tuyau par le prédéterminé montant xLoc1-= X_MOVEMENT_DIFFERENCE ; xLoc2 = X_MOVEMENT_DIFFERENCE ; si (birdFired & &! isSplash) {birdYTracker = birdY ; birdFired = false;} si () birdThrust & &! isSplash) {//move oiseaux verticalement si (birdYTracker - birdY - BIRD_JUMP_DIFF < BIRD_JUMP_HEIGHT) {si (birdY - BIRD_JUMP_DIFF > 0) {birdY = BIRD_JUMP_DIFF ; //coordinates différents} autre {birdY = 0; birdYTracker = birdY ; birdThrust = false;}} else {birdYTracker = birdY ; birdThrust = false;}} ElseIf ()! isSplash) {birdY += BIRD_FALL_DIFF ; birdYTracker = birdY;} //update la BottomPipe et bp1.setX(xLoc1) de TopPipe de lieux ; BP1.Sety(yLoc1) ; BP2.setX(xLoc2) ; BP2.Sety(yLoc2) ; TP1.setX(xLoc1) ; TP1.Sety(yLoc1-PIPE_GAP-PIPE_HEIGHT) ; assurer le tp1 placé au bon endroit tp2.setX(xLoc2) ; TP2.Sety(yLoc2-PIPE_GAP-PIPE_HEIGHT) ; assurer le tp2 placé au bon endroit if(!isSplash) {bird.setX(birdX) ; bird.setY(birdY) ; pgs.setBird(bird);} //set la BottomPipe et TopPipe des variables locales en PlayGameScreen par l’analyse de la pgs.setBottomPipe de variables locales (bp1, bp2) ; pgs.setTopPipe (tp1, tp2) ; Si (! isSplash & & bird.getWidth()! = -1) {//need la deuxième partie car si les oiseaux pas à l’écran, ne peuvent pas obtenir la largeur de l’image et ont en cascade erreur dans collisionDetection de collision (bp1, bp2, tp1, tp2, oiseau); updateScore (bp1, bp2, oiseaux);} //update pgs le JPanel topPanel.revalidate() ; topPanel.repaint() ; mettre à jour la variable suivi du temps après toutes les opérations terminées startTime = System.currentTimeMillis() ; }}} / ** * Calcule un int aléatoire pour le placement de la pipe bas * int * / private int bottomPipeLoc() {int temp = 0; //iterate jusqu'à ce que le temp est une valeur qui autorise les deux tuyaux être à l’écran tout en (temp < = PIPE_GAP + 50 || temp > = SCREEN_HEIGHT-PIPE_GAP) {temp = (int) ((double) Math.random()*((double)SCREEN_HEIGHT));} return temp;} / ** * méthode qui vérifie si la partition doit être mis à jour * bp1 BottomPipe premier objet * bp2 BottomPipe deuxième objet * objet oiseau oiseau * / private void updateScore (BottomPipe bp1 BottomPipe bp2, oiseau oiseaux) {if(bp1.getX() + PIPE_WIDTH < bird.getX() & & bp1.getX() + PIPE_WIDTH > bird.getX() - X_MOVEMENT_DIFFERENCE) {pgs.incrementJump();} else if(bp2.getX() + PIPE_WIDTH < bird.getX() & & bp2.getX() + PIPE_WIDTH > bird.getX() - X_MOVEMENT_DIFFERENCE) {pgs.incrementJump();}} / ** * méthode pour tester si une collision s’est produite * bp1 BottomPipe premier objet * bp2 BottomPipe deuxième objet * tp1 TopPipe premier objet * tp2 TopPipe deuxième objet * objet oiseau passereau * / private void collisionDetection (BottomPipe bp1 BottomPipe bp2, TopPipe tp1, tp2 TopPipe, oiseau passereau) {collisionHelper(bird.getRectangle(), bp1.getRectangle(), bird.getBI(), bp1.getBI()) ; collisionHelper(bird.getRectangle(), bp2.getRectangle(), bird.getBI(), bp2.getBI()), collisionHelper(bird.getRectangle(), tp1.getRectangle(), bird.getBI(), tp1.getBI()) ; collisionHelper(bird.getRectangle(), tp2.getRectangle(), bird.getBI(), tp2.getBI()) ; if(bird.getY() + BIRD_HEIGHT > SCREEN_HEIGHT * 7/8) {//ground détection pgs.sendText ("Game Over"); loopVar = false ; gamePlay = false ; //game a pris fin}} / ** * méthode d’assistance au test de collision potentielle de l’objet de l’oiseau avec un objet de canal. * de r1 l’oiseau composant rectangle * r2 rectangle de Collision composant * composant BufferedImage de b1 l’oiseau * b2 composant de Collision BufferedImage * / private void collisionHelper (Rectangle r1, r2 Rectangle, BufferedImage b1, b2 BufferedImage) {if(r1.intersects(r2)) {Rectangle r = r1.intersection(r2) ; int firstI = (int) (r.getMinX() - r1.getMinX()) ; //firstI est le premier x-pixel pour itérer au sein de l’int firstJ = (int) (r.getMinY() - r1.getMinY()) ; //firstJ est le premier y-pixel pour itérer au sein de l’int bp1XHelper = (int) (r1.getMinX() - r2.getMinX()) ; //helper variables à utiliser en se référant à la collision objet int bp1YHelper = (int) (r1.getMinY() - R2.getMinY()) ; pour (int j’ai = firstI ; j’ai < r.getWidth() + firstI; i ++) {/ / for (int j = firstJ; j < r.getHeight() + firstJ; j ++) {si ((b1.getRGB (i, j) & 0xFF000000)! = 0 x 00 & & (b2.getRGB (i + bp1XHelper, j + bp1YHelper) & 0xFF000000)! = 0 x 00) {pgs.sendText ("Game Over"); loopVar = false ; //stop le gamePlay de la boucle de jeu = false ; //game a terminé break;}}}} } }