Lekce 14 - Programování jednoduchých Java GUI her - AutoHra vysvětlení
V předchozí lekci, Programování jednoduchých Java GUI her - AutoHra pokračování, jsme pokračovali v AutoHře.
Vysvětlení kódu hry AutoHra. Při spuštění programu se nejdříve najde metoda main(). Ta je ve třídě AutoHra.java
public static void main(String[] args) { AutoHra hlavniOkno = new AutoHra();
Zde se vytvoří hlavní okno (zavolá se konstruktor třídy AutoHra).
public AutoHra() { this.setTitle("AutoHra"); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); HerniPanel panel = new HerniPanel();
Titulek okna se nastaví na AutoHra, nastaví se také, co se má stát po kliknutí na křížek okna (zavření). Vytvoří se panel (zavolá se konstruktor třídy HerniPanel). Nejdříve se však deklarují proměnné (některé rovnou inicializují).
private int sirkaPanelu = 400; private int vyskaPanelu = 600; private Timer casovac; private Auto auto; private boolean hrajeSe; private int citac; private int prodlevaMeziPrekazkami; private List<Prekazka> prekazky; public HerniPanel() { init(); start(); }
V konstruktoru panelu je volána metoda init().
private void init() { this.setPreferredSize(new Dimension(sirkaPanelu, vyskaPanelu)); this.setBackground(Color.black); this.setForeground(Color.white); this.setFont(new Font(Font.MONOSPACED, Font.BOLD, 20)); this.setFocusable(true); }
V ní se nastavuje preferovaná velikost JPanelu, barva pozadí, barva popředí, písmo a také zaměření (focus). Metoda se provede a činnost se vrací zpět do konstruktoru. Po metodě init() následuje metoda start().
private void start() { hrajeSe = true; auto = new Auto(this);
Nastaví se proměnná hrajeSe a zavolá se konstruktoru třídy Auto.
private HerniPanel panel; private Image autoObr; private int x; private int y; private int dx; public Auto(HerniPanel panel) { ImageIcon ii = new ImageIcon(this.getClass().getResource("auto.png")); autoObr = ii.getImage(); this.panel = panel; this.x = 185; this.y = 558; this.dx = 0; }
Načte se obrázek a vloží do proměnné autoObr (do proměnné se uloží reference na objekt typu Image, který představuje obrázek auta). Konstruktor má jeden parametr a to odkaz na JPanel, který ho zavolal. Parametr jsme nazvali panel a uložíme ho do proměnné panel objektu Auto (this.panel = panel). Nastavíme hodnoty proměnným x, y, dx.
private void start() { ... auto = new Auto(this); citac = 0; prodlevaMeziPrekazkami = 100; prekazky = new ArrayList<Prekazka>(); this.addKeyListener(auto); casovac = new Timer(10, this); casovac.start(); }
Konstruktor provedl nadefinovanou činnost a řízení se vrací zpět do metody start(). Reference (odkaz) na objekt typu Auto se uloží do proměnné auto, proměnná čítač se nastaví na nulu, prodleveMeziPrekazkami na 100 a do proměnné prekazky se uloží odkaz na objekt typu ArrayList (volá se konstruktor třídy ArrayList). Objekt Auto (odkaz na něj je v proměnné auto) si panel zaregistruje jako posluchače událostí z klávesnice. Vytvoří se objekt typu Timer a spustí se (Timer si jako posluchače registruje aktuální objekt, ve kterém je vytvářen this = HerniPanel). Tím je ukončena metoda start.
Řízení se vrací zpět od konstruktoru třídy AutoHra.
public AutoHra() { ... this.add(panel); this.setResizable(false); this.pack(); }
Do okna je přidán panel, okno se nastaví tak, aby jeho velikost nešla měnit a volá se metoda pack(), která zjistí velikosti komponent obsažených v okně a zabalí okno okolo nich (přizpůsobí velikost okna komponentám).
public static void main(String[] args) { AutoHra hlavniOkno = new AutoHra(); hlavniOkno.setVisible(true); }
Objekt typu AutoHra je vytvořen a odkaz na něj je uložen do proměnné hlavniOkno. Pak je na tomto objektu zavolána jeho metoda setVisible().
@Override public void paintComponent(Graphics g) { // pokud se hraje if (hrajeSe) { super.paintComponent(g); vypisCitac(g); auto.vykresliSe(g); for (int i = 0; i < prekazky.size(); i++) { Prekazka prek = prekazky.get(i); prek.vykresliSe(g); } } // pokud hra skončila else { vypisKonec(g); } }
Až se systém rozhodne, že je čas vykreslit komponenty (což může být někdy v době, kdy je volána metoda JFrame.setVisible()), je zavolána metoda paintComponent(). Ta je automaticky volána při vzniku JPanelu (při jeho prvotním vykreslení). Jelikož proměnná hrajeSe = true, provede část v klauzuli if. Vykreslí se pozadí, zavolá se metoda pro vypsání (vykreslení) stavu čítače, zavolá se metoda vykresliSe objektu Auto, která vykreslí obrázek na aktuální souřadnice a cyklus for neproběhne ani jednou, jelikož velikost seznamu je nula.
Tak a máme zobrazenou aplikaci. Aplikace čeká. Teď přichází na řadu Timer. Co 10 milisekund vygeneruje událost typu ActionEvent a pošle ji registrovanému posluchači (zavolá metodu actionPerformed() daného posluchače). Jako posluchač těchto událostí je zaregistrován náš herní panel.
public void actionPerformed(ActionEvent ae) { citac++; auto.provedPohyb(); pridejPrekazku();
Navýší čítač o jedničku, zavolá metodu provedPohyb na objektu, jehož reference je uložena v proměnné auto, a která provede změnu souřadnic (záleží na stiskuté klávese). Zavolá metodu pro přidávání překážek.
private void pridejPrekazku() { if (citac == 1000) { prodlevaMeziPrekazkami = 70; } if (citac == 2000) { prodlevaMeziPrekazkami = 50; } if (citac == 3000) { prodlevaMeziPrekazkami = 40; } if (citac == 5000) { prodlevaMeziPrekazkami = 30; } if (citac == 7500) { prodlevaMeziPrekazkami = 20; } if (citac == 10000) { prodlevaMeziPrekazkami = 10; } if ((citac % prodlevaMeziPrekazkami) == 0) { Prekazka prek = new Prekazka(this); prekazky.add(prek); } }
Ta podle stavu čítače upraví proměnnou prodlevaMeziPrekazkami a následně zkontroluje, zda zbytek po dělení čítače a prodlevaMeziPrekazkami je nula (např. 402 % 200 = 2, 1000 % 50 = 0). V případě že ano, vytvoří objekt typu Prekazka a referenci na něj přidá do seznamu.
public void actionPerformed(ActionEvent ae) { ... pohniPrekazkami(); ...
Běh programu se vrací zpět do metody actionPerformed a volá se metoda pohniPrekazkami().
private void pohniPrekazkami() { for (int i = 0; i < prekazky.size(); i++) { Prekazka prek = prekazky.get(i); prek.provedPohyb(); } }
Postupně projede seznamem a zavolá na každém objektu typu Prekazka jeho metodu provedPohyb().
public void actionPerformed(ActionEvent ae) { ... if (isSrazka()) { hrajeSe = false; casovac.stop(); } odstranPrekazkyKtereJsouMimo(); this.repaint(); }
Běh programu se vrací zpět do metody actionPerformed. V podmínce if je volána metoda isSrazka(), která vrací true, v případě, že došlo ke střetu chytače s některou překážkou. Pokud ano mění se hodnota proměnné hrajeSe na false a zastaví se časovač. Následně se volá metoda, která odstraní překážky, které jsou mimo (jejich proměnná viditelny = false). Jako poslední se volá metoda repaint(), která kromě dalších věcí volá metodu paintComponent(). Ta v závisloti na stavu proměnné hrajeSe provede vykreslení.
Výše uvedené se provádí do té doby, dokud je časovač (Timer) v činnosti.