Lekce 8 - Programování jednoduchých Java GUI her - Časovač 2
V minulé lekci, Programování jednoduchých Java GUI her - Časovač, jsme si rozpohybovali objekt na JPanelu pomocí časovače a tím jsme si vytvořili animaci.
V tomto díle budeme pokračovat v tom, co jsme začali minule - pohyb objektů po JPanelu.
Vytvoříme program, ve kterém budeme pohybovat objektem (v tomto případě obdélníkem) pomocí klávesnice.
import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import javax.swing.JPanel; import javax.swing.Timer; public class MujPanel extends JPanel { private final int SIRKA_OBDELNIKA = 80; private final int VYSKA_OBDELNIKA = 50; private int x, y; private int smerX, smerY; private int rychlost; private Rectangle obdelnik; public MujPanel() { this.x = 0; this.y = 0; this.smerX = 0; this.smerY = 0; this.rychlost = 2; this.obdelnik = new Rectangle(x, y, SIRKA_OBDELNIKA, VYSKA_OBDELNIKA); this.setPreferredSize(new Dimension(400, 300)); this.setBackground(Color.blue); this.setFocusable(true); this.addKeyListener(new PosluchacKlavesnice()); Timer casovac = new Timer(10, new PosluchacCasovace()); casovac.start(); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(Color.red); g.drawRect(x, y, SIRKA_OBDELNIKA, VYSKA_OBDELNIKA); } private void move() { x += smerX; y += smerY; if (x >= this.getWidth() - (SIRKA_OBDELNIKA + 1)) { x = this.getWidth() - (SIRKA_OBDELNIKA + 1); } if (x <= 0) { x = 0; } if (y >= this.getHeight() - (VYSKA_OBDELNIKA + 1)) { y = this.getHeight() - (VYSKA_OBDELNIKA + 1); } if (y <= 0) { y = 0; } } private class PosluchacCasovace implements ActionListener { @Override public void actionPerformed(ActionEvent e) { move(); repaint(); } } private class PosluchacKlavesnice implements KeyListener { @Override public void keyPressed(KeyEvent e) { int klavesa = e.getKeyCode(); if (klavesa == KeyEvent.VK_LEFT) { smerX = -rychlost; } else if (klavesa == KeyEvent.VK_UP) { smerY = -rychlost; } else if (klavesa == KeyEvent.VK_RIGHT) { smerX = rychlost; } else if (klavesa == KeyEvent.VK_DOWN) { smerY = rychlost; } } @Override public void keyReleased(KeyEvent e) { int klavesa = e.getKeyCode(); if (klavesa == KeyEvent.VK_LEFT) { smerX = 0; } else if (klavesa == KeyEvent.VK_UP) { smerY = 0; } else if (klavesa == KeyEvent.VK_RIGHT) { smerX = 0; } else if (klavesa == KeyEvent.VK_DOWN) { smerY = 0; } } @Override public void keyTyped(KeyEvent e) { } } }
private final int SIRKA_OBDELNIKA = 80; private final int VYSKA_OBDELNIKA = 50;
Deklarujeme dvě proměnné typu int a inicializujeme je na zadané hodnoty. Klíčové slovo final znamená, že tyto proměnné jsou konstanty (nebude se měnit jejich hodnota). Takovýmto způsobem deklarujeme proměnné, u kterých víme, že jejich hodnota je konečná.
Tento způsob deklarace je výhodný z toho důvodu, že v případě, že se v programu omylem pokusíme změnit hodnoty této proměnné, překladač nám to neumožní.
private int x, y;
X-ová a y-nová souřadnice levého horního rohu obdélníka.
private int smerX, smerY;
Proměnné uchovávající posun ve směru. Pokud jsou nula obdélník se nepohybuje.
private int rychlost;
Proměnná uchovávající rychlost, jakou se bude obdélník pohybovat.
this.obdelnik = new Rectangle(x, y, SIRKA_OBDELNIKA, VYSKA_OBDELNIKA);
Proměnná obdelnik je typu Rectangle. Do konstruktoru zadáváme souřadnice levého horního rohu (x, y), šířku a výšku obdélníka.
this.addKeyListener(new PosluchacKlavesnice());
JPanel (this) si registruje jako posluchače událostí KeyEvent objekt typu PosluchacKlavesnice.
Jinak řečeno, objektu PosluchacKlavesnice, který byl právě vytvořen pomocí new, se budou posílat události (zprávy) typu KeyEvent. Je to totéž, jako by jste napsali:
PosluchacKlavesnice posKla = new PosluchacKlavesnice(); this.addKeyListener(posKla);
Ušetří se tím ale trocha psaní.
Timer casovac = new Timer(10, new PosluchacCasovace()); casovac.start();
Nic nového, ale opakování je matka moudrosti. Vytvoříme nový Timer, který bude generovat události co deset milisekund a posluchačem těchto událostí bude právě vytvořený objekt typu PosluchacCasovace.
public void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(Color.red); g.drawRect(x, y, SIRKA_OBDELNIKA, VYSKA_OBDELNIKA); }
Vykreslí pozadí, nastaví barvu popředí na červenou a vykreslí obdélník (Rectangle). Obdélník rozpohybujeme tak, že mu budeme měnit souřadnice x a y a opětovně ho kreslit na JPanel.
x += smerX; y += smerY;
V metodě move(), když je zavolána, se x a y změní podle hodnoty v proměnných smerX, smerY. Tyto dvě proměnné jsou v kostruktoru inicializovány na nulu, takže obdélník se nepohybuje. Pomocí změn těchto proměnných ovlivňujeme pohyb obdélníka. Měnit je budeme při stisku kláves.
Následně v metodě move() kontrolujeme, zda je obdélník (celá jeho plocha) viditelný. Možná vám není jasné, proč přičítáme ještě jedničku (x >= this.getWidth() - (SIRKA_OBDELNIKA + 1)). X-ová souřadnice začína nulou a při nastavení velikosti panelu na 400 bodů je viditelné x od 0 do 399. Výsledkem operace je 320 (x-ová souřadnice levého horního rohu obdélníka) a když 320 sečteme s 80 (šířkou obdélníka) dostaneme 400, což je pravý okraj obdélníka, který je však již není viditelný. Pokud to budete chtít zkusit a odmažete jedničku, přesto se obdélník může zobrazovat správně. Je to proto, že velikost nastavená metodou setPreferredSize() není zcela závazná a JPanel může mít velikost jinou. Aktuální velikost JPanelu získáte metodou getWidth() a getHeight().
public void keyPressed(KeyEvent e) { int klavesa = e.getKeyCode(); if (klavesa == KeyEvent.VK_LEFT) { smerX = -rychlost; } else if (klavesa == KeyEvent.VK_UP) { smerY = -rychlost; } else if (klavesa == KeyEvent.VK_RIGHT) { smerX = rychlost; } else if (klavesa == KeyEvent.VK_DOWN) { smerY = rychlost; } }
V případě stisku klávesy na klávesnici je vygenerována událost KeyEvent, která je zaslána zaregistrovanému posluchači (je zavolána posluchačova metoda keyPressed() a jako parametr je jí předán objekt typu KeyEvent).
int klavesa = e.getKeyCode(); if (klavesa == KeyEvent.VK_LEFT) { smerX = -rychlost; }
Získáme číslo reprezentující klávesu. Následně toto číslo porovnáváme s konstantou. Pokud se například proměnná klávesa rovná 37, jedná se o klávesu nenumerická šipka vlevo (37 je hodnota konstanty KeyEvent.VK_LEFT). V tomto případě proměnná smerX bude -rychlost.
Proměnná rychlost je dva, tím pádem při zavolání metody move(), které se děje při každém vygenerování událostí objektem Timer (dle našeho nastavení každých 10 milisekund), se bude hodnota souřadnice x snižovat o dva a obdélník se bude pohybovat doleva.
V metodě keyPressed(KeyEvent e) ošetřujeme stav, kdy je stisknuta nějaká z nenumerických šipek. Obdobné je to s metodou keyReleased(KeyEvent e). Pokud bychom tuto metodu nechali prázdnou, obdélník by se pohyboval i po puštění šipky.
//super.paintComponent(g);
Pokud jste to ještě nezkusili, tak zadokumentujte (nebo vymažte) volání rodičovské metody paintComponent. Tak nejlépe zjistíte, k čemu to vlastně je.
V příští lekci, Programování jednoduchých Java GUI her - Detekce kolizí, si ukážeme, jak detekovat kolize objektů pomocí třídy Rectangle a metody Intersect.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 330x (1.61 kB)
Aplikace je včetně zdrojových kódů v jazyce Java