8. díl - Programování jednoduchých Java GUI her - Časovač 2

Java Tvorba her Programování jednoduchých Java GUI her - Časovač 2

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) {

        }
    }
}
Pohyb pomocí klávesnice v Java Swing
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 PosluchacKlaves­nice.

Jinak řečeno, objektu PosluchacKlaves­nice, 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(Ke­yEvent 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.


 

Stáhnout

Staženo 275x (1.61 kB)
Aplikace je včetně zdrojových kódů v jazyce Java

 

  Aktivity (1)

Článek pro vás napsal vita
Avatar
vita

Jak se ti líbí článek?
Celkem (1 hlasů) :
55555


 



 

 

Komentáře
Zobrazit starší komentáře (2)

Avatar
Kit
Redaktor
Avatar
Kit:

Chybí tam toho víc. Například proměnná serialVersionUID. To ti nepíše žádné warningy nebo co?

Odpovědět 28.9.2013 18:38
Vlastnosti objektů by neměly být veřejné. A to ani prostřednictvím getterů/setterů.
Avatar
Jan Vargovský
Redaktor
Avatar
Odpovídá na Kit
Jan Vargovský:

Nějak jsem si nevšiml, že by něco serializoval.

 
Odpovědět 28.9.2013 19:18
Avatar
Kit
Redaktor
Avatar
Odpovídá na Jan Vargovský
Kit:

Pokud děláš potomka třídy JPanel, vyžaduje deklaraci proměnné serialVersionUID. Najdeš to i v dokumentaci třídy JPanel.

Asi máš vypnuté warningy, jinak bys o tom určitě věděl.

Odpovědět  -1 28.9.2013 19:38
Vlastnosti objektů by neměly být veřejné. A to ani prostřednictvím getterů/setterů.
Avatar
Kit
Redaktor
Avatar
Odpovídá na Jan Vargovský
Kit:

Ještě odkazy na dokumentaci:
http://docs.oracle.com/…/JPanel.html
Zde je napsáno, že JPanel implementuje Serializable
http://docs.oracle.com/…lizable.html
a zde je uvedeno, že deklarace a definice serialVersionUID je strongly recommended a proto se vypisují ta varovná hlášení, která sis možná ve svém IDE vypnul.

Odpovědět 28.9.2013 20:00
Vlastnosti objektů by neměly být veřejné. A to ani prostřednictvím getterů/setterů.
Avatar
Jan Vargovský
Redaktor
Avatar
Odpovídá na Kit
Jan Vargovský:

Já v Jave nedělám, jen jsem vygooglil co ta proměnná vůbec má dělat a dočetl jsem se, že je to k serializaci -> usoudil jsem, že je to asi zbytečné, ale ten tvůj post dává taky smysl.

 
Odpovědět 28.9.2013 20:08
Avatar
Kit
Redaktor
Avatar
Odpovídá na Jan Vargovský
Kit:

Začalo to tím, že mi Java furt vyhazovala warningy. Tak jsem si na to v editoru udělal automatickou korekturu, která mi tam ten serialVersionUID sama dopíše - kromě spousty dalších blbinek, jako např. opravy ,pr na System.out.println(), vygenerování testu, stubu, konstruktoru, metody main apod.

Odpovědět 28.9.2013 20:16
Vlastnosti objektů by neměly být veřejné. A to ani prostřednictvím getterů/setterů.
Avatar
vita
Redaktor
Avatar
Odpovídá na Kit
vita:

Pokud budeme trvat na doporučeních, tak samozřejmě serialVersionUID by tam být mělo. Děkuji za upozornění a souhlasím. Na druhou stranu žádný objekt neserializujeme a tím pádem si myslím, že je zbytečné v tutoriálu vysvětlovat, co znamená řádek static final long serialVersionUID a zabíhat do tématu serializace objektů.

 
Odpovědět 29.9.2013 12:58
Avatar
Kit
Redaktor
Avatar
Odpovídá na vita
Kit:

Prostě jsem se podivoval nad tím, proč má někdo v IDE vypnuté warningy. Nic víc.

Odpovědět 29.9.2013 13:02
Vlastnosti objektů by neměly být veřejné. A to ani prostřednictvím getterů/setterů.
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Kit
David Čápka:

V NetBeans se ten warning nezobrazuje ve výchozí konfiguraci, asi ho Oracle nepovažuje za důležitý.

Odpovědět 29.9.2013 13:11
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
Kit
Redaktor
Avatar
Odpovídá na David Čápka
Kit:

Tak už jsem na to přišel. Když kompiluji přímo ze svého editoru, tak spouštím kompilátor s parametrem -Xlint, který zapíná všechny warningy. Včetně těch, které nejsou defaultní. Výsledkem tedy je, že vše, co vyprodukuji, je javovsky správné, ale když kompiluji cizí kód, vyhazuje to spoustu warningů nebo dokonce errorů, které autor vůbec neviděl.

Když jsem cvičně zkompiloval program, ze kterého jsem vyhodil serialVersionUID bez dalších parametrů, žádný warning nevyskočil. Tímto se omlouvám za mystifikaci. Prostě mám pro sebe nastavena ta nejpřísnější pravidla kompilace, abych se naučil Javu správně a nějak jsem na to zapomněl.

Odpovědět 29.9.2013 13:46
Vlastnosti objektů by neměly být veřejné. A to ani prostřednictvím getterů/setterů.
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 12. Zobrazit vše