Oefening 6: En nog eens objectoriëntatie

Doel

Bouw een grafisch spel gebaseerd op Java Swing klassen.

Opdracht

Maak Flappy Bird.

Swing JFrame

  1. Maak een klasse FlappyBirdGame.
  2. Importeer alle klassen van java.awt, javax.swing, en java.awt.event.
  3. FlappyBirdGame erft van JFrame. Dit vormt het venster van de GUI.
  4. Maak een main methode. Maak een nieuw FlappyBirdGame object aan.
  5. Maak een constructor voor FlappyBirdGame. De constructor:
    1. stelt een hoogte en breedte in,
    2. definieert een default sluit operatie,
    3. geeft een titel op,
    4. zorgt dat het frame niet kan veranderen van grootte.
    5. en maakt het frame zichtbaar.
  6. Voer de applicatie uit.
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;

public class FlappyBirdGame extends JFrame {
  public FlappyBirdGame() {
        setSize(600, 600);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setTitle("Flappy Bird");

        setResizable(false);
        setVisible(true);
    }

    public static void main(String[] args) {
         new FlappyBirdGame();        
    }
}

Swing JPanel

  1. Maak binnen de FlappyBirdGame klasse een private GamePanel klasse die erft van JPanel. Het panel voorziet de ruimte in het venster waarin componenten toegevoegd kunnen worden en waarop getekend kan worden.
  2. In de FlappyBirdGame constructor, maak een object van GamePanel en roep de setContentPane methode aan met dit object als parameter. Deze methode voegt het panel toe aan het frame.
public FlappyBirdGame() {
  setSize(600, 600);
  setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  setTitle("Flappy Bird");
  JPanel paneel = new GamePanel();
    setContentPane(paneel);

  setResizable(false);
  setVisible(true);
}

private class GamePanel extends JPanel{

}

Swing graphics

  1. Alle grafische operaties vinden plaats in de paintComponent methode van JPanel. Het Graphics object voorziet methodes hiervoor.

    1. Overschrijf de methode paintComponent, deze methode verwacht een Graphics object als parameter.
    2. Roep de paintComponent methode aan van de super klasse. Geef de parameter door.
    3. De methode setColor voorziet een kleur.
    4. De methode setFont selecteert een font, met stijl en grootte.
    5. De methode drawString, plaatst een String op een coördinaat (x,y). Het coördinaat (0,0) bevindt zich in de linkerbovenhoek van het panel, waarbij de richting van de x-as naar rechts is geörienteerd en de y-as naar beneden. Ook bij de figuren zelf wordt het coördinaat (0,0) in de linkerbovenhoek van de figuur gesitueerd.
    private class GamePanel extends JPanel{
     public void paintComponent(Graphics g) {
               super.paintComponent(g);
    
         g.setColor(Color.ORANGE);
             g.setFont(new Font("Consolas", 1, 50));
             g.drawString("Hello World!", 150, 50);            
           }
    }
    
  2. Plaats een zwarte rechthoek over de hele breedte van het panel onderaan. Dit stelt de grond voor. Gebruik hiervoor de methode fillRect
  3. Teken een groene cirkel, die de vogel voorstelt, ongeveer in het midden van het panel. Maak hiervoor gebruik van Ellipse2D.Double.

    1. Importeer Ellipse2D.Double: import java.awt.geom.Ellipse2D;
    2. Cast het Graphics object naar een Graphics2D object.
    3. Roep de fill methode aan op het Graphics2D object en maak een nieuw Ellipse2D.Double object aan. Geef het coördinaat en de straal op.
    private class GamePanel extends JPanel{
     public void paintComponent(Graphics g) {
         super.paintComponent(g);
    
         Graphics2D g2d = (Graphics2D) g;
         g2d.fill(new Ellipse2D.Double(x,y,r,r));            
       }
    }
    

    Resultaat voor de initiële GUI

    Figuur 1: Resultaat voor de initiële GUI.

Animatie

  1. Animatie wordt voorzien door om een aantal milliseconden het panel opnieuw te tekenen. Een Timer object genereert periodisch een event. Na dit event wordt een "handler" methode aangeroepen.

    1. Maak een Timer attribuut in de GamePanel klasse.
    2. Voorzie een constructor voor GamePanel. Maak een Timer object, stel hierbij een periode in en registreer een TimerHandler object.
    3. Roep de start methode aan op het Timer object.
    4. Maak binnen de GamePanel klasse een TimerHandler klasse die een ActionListener implementeert.
    5. Maak in de TimerHandler klasse een methode void actionPerformed(ActionEvent e). Deze methode wordt aangeroepen na het timerevent.
    6. Roep de repaint() methode aan op het einde actionPerformed methode. Deze tekent het panel opnieuw.
    private class GamePanel extends JPanel{
     private Timer timer;
    
     public GamePanel(){
       timer = new Timer(20, new TimerHandler());
       timer.start();
     }
    
     class TimerHandler implements ActionListener{
         //Deze methode wordt aangeroepen na het timer event
         public void actionPerformed(ActionEvent e){
    
               repaint();
         }
     }
           //...
    }
    
  2. De toestand, i.e. het x,y coördinaat en afmetingen, van de verschillende componenten in de GUI wordt het best in aparte objecten bijgehouden. Voorzie een klasse voor FlappyBird.

    1. Definieer een teken(Graphics g) methode, die de component op het grafisch object tekent.
    2. Voorzie een methode val die het y attribuut aanpast.

      private class GamePanel extends JPanel{
      private Timer timer;
      private FlappyBird vogel;
      
      public GamePanel(){
      vogel = new FlappyBird(285,285);
      timer = new Timer(20, new TimerHandler());
      timer.start();
      }
      
      class TimerHandler implements ActionListener{
        //Deze methode wordt aangeroepen na het timer event
        public void actionPerformed(ActionEvent e){
              vogel.val();
              repaint();
        }
      }
          //...
      }
      
      import java.awt.*;
      import java.awt.geom.Ellipse2D;
      public class FlappyBird
      {
        private int x,y,r;   
      
        public FlappyBird(int x, int y){
            this.x = x;
            this.y = y;
            r = 30;
        }
      
        public void val(){
            y += 4;
        }
      
        public void teken(Graphics g){
            g.setColor(Color.GREEN);
            Graphics2D g2d = (Graphics2D) g;
            g2d.fill(new Ellipse2D.Double(x,y,r,r));
        }
      }
      
  3. Maak ook klassen voor Rechthoek en Tekst.

Invoer

  1. Invoer kan via het keyboard of de muis. Alle invoer wordt als een event geworpen en er moet een "handler" methode voorzien worden om deze af te kunnen handelen. In Swing worden alle events gesynchroniseerd, dit wil zeggen na elkaar afgehandeld, waardoor een Timer event geen Mouse event zal onderbreken of omgekeerd.

    1. De GamePanel klasse implementeert de MouseListener interface.
    2. Voeg mouseClicked, mousePressed, mouseReleased, mouseEntered en mouseExited methodes toe.
    3. Registreer de MouseListener in de GamePanel constructor met addMouseListener(this);
    private class GamePanel extends JPanel implements MouseListener {
     //...
     public GamePanel(){
       //...
       addMouseListener(this);
     }
    
      public void mouseClicked(MouseEvent e) {
        //Voeg hier code toe die het klikken van de muis afhandelt
      }
    
      public void mousePressed(MouseEvent e) {
      }
    
      public void mouseReleased(MouseEvent e) {
      }
    
      public void mouseEntered(MouseEvent e) {
      }
    
      public void mouseExited(MouseEvent e) {
      }
    }
    
  2. FlappyBird begint pas te vallen als er een eerste keer werd geklikt. Laat de timer pas starten als de gebruiker met de muis klikt. Eventueel kan je controleren of de timer nog niet reeds gestart werd.

    public void mouseClicked(MouseEvent e) {
     if(!timer.isRunning()) timer.start();
    }
    
  3. Als de gebruiker met de muis klikt, moet de FlappyBird omhoog springen. Voeg een spring methode toe aan de FlappyBird klasse en roep deze aan in de mouseClicked handler.

Botsingdetectie

  1. Als de FlappyBird de grond raakt, of in een latere fase 1 van de obstakels, stopt het spel. Hiervoor moet gecontroleerd worden of de verschillende spelcomponenten elkaar niet overlappen. Om detectie te vereenvoudigen wordt typisch gewerkt met een boundingbox, dit is een omhullende rechthoek van de figuur.

    1. Laat de FlappyBird klasse erven van Ellipse2D.Double, verwijder de attributen en roep de super constructor aan. Hierdoor moet niet steeds opnieuw een nieuw Ellipse2D.Double object aangemaakt worden om de FlappyBird te tekenen, of de boundingbox te berekenen.
    2. Voeg de methode botst toe aan de klasse FlappyBird. Controleer hierin of de boundingbox van het FlappyBird object snijdt met een Rectangle.

      public class FlappyBird extends Ellipse2D.Double
      {
        public FlappyBird(int x, int y){
          super(x,y,30,30);
        }
      
        public void teken(Graphics g){
          g.setColor(Color.GREEN);
          Graphics2D g2d = (Graphics2D) g;
          g2d.fill(this);
        }
      
        public boolean botst(Rectangle r) {
          return r.intersects(this.getBounds2D());
        }
      
        //...
      }
      
    3. Laat de klasse Rechthoek erven van de klasse Rectangle. Maak de nodige aanpassingen, vergelijkbaar met stap 1.

    4. Controleer na het vallen van de FlappyBird in de timer handler methode of de FlappyBird botst met de grond. Indien ja, stop de timer.

Spel

  1. Voeg obstakels toe. Dit zijn twee rechthoeken waartussen een opening wordt gelaten, waartussen de FlappyBird moet vliegen.
  2. In plaats van FlappyBird vooruit te laten gaan, verplaats per timer event de obstakels naar links.
  3. Indien de FlappyBird botst met een obstakel is het spel afgelopen.
  4. Na afloop, start het spel opnieuw.

Extra

  1. Hou bij hoeveel obstakels succesvol voorbij werden gegaan.
  2. Hou een topscore bij.
  3. In plaats van figuren, maak gebruik van ImageIcon
  4. In plaats van een vaste waarde te vallen en omhoog te springen, geef een verandering in versnelling.

results matching ""

    No results matching ""