Hledáme nového kolegu do redakce - 100% home office, 100% flexibilní pracovní doba. Více informací.
Využij akce až 80 % zdarma při nákupu e-learningu - více informací. Zároveň pouze tento týden sleva až 80 % na e-learning týkající se Swift

Diskuze: Moja prvá hra - Arkanoid

Aktivity
Avatar
Neaktivní uživatel:22.8.2020 14:25

Ahojte,

práve som urobila jednu z mojich prvých hier. Je to Arkanoid.

Zkusil jsem: Takmer všetko mi ide ako má.

Chci docílit: Chcela by som len dostať úprimný názor na môj kód, poprípade nejaké rady ako ho môžem vylepšiť. :)

Odpovědět
22.8.2020 14:25
Neaktivní uživatelský účet
Avatar
Neaktivní uživatel:22.8.2020 14:27
public class Breaker {
    private BlockBreakerPanel panel;
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        Breaker b = new Breaker();
        b.zobraz();
    }

    private void zobraz() {
        JFrame frame = new JFrame("Block Breaker");
        frame.setSize(490, 600);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        this.panel = new BlockBreakerPanel(this);
        frame.add(this.panel);
        frame.setVisible(true);
    }

    public BlockBreakerPanel getPanel() {
        return this.panel;
    }
}

class BlockBreakerPanel extends JPanel implements KeyListener, ActionListener {

    private ArrayList<Blocks> blocks = new ArrayList<>();
    private final Paddle hrac;
    private final Ball ball;
    private final Breaker game;
    private boolean gameOver;
    private int skore;
    private boolean start;

    public BlockBreakerPanel(Breaker game) {
        setBackground(Color.BLACK);

        for (int rad = 0; rad < 3; rad++) {
            for (int i = 0; i < 8; i++) {
                this.blocks.add(new Blocks(i * 60 + 2, rad * 40 + 5, 50, 30));
            }
        }

        this.hrac = new Paddle(game);
        this.ball = new Ball(game);
        this.gameOver = false;
        this.skore = 0;
        this.start = false;

        Timer t = new Timer(5, this);
        t.start();

        addKeyListener(this);
        setFocusable(true);
        this.game = game;
    }

    public void pridajBod() {
        this.skore++;
    }

    public void jeKoniec() {
       this.gameOver = true;
    }

    public ArrayList<Blocks> getBlocks() {
        return this.blocks;
    }
    public Paddle getHrac() {
        return this.hrac;
    }

    public void paint(Graphics g) {
        super.paintComponent(g);

        this.blocks.forEach((block) -> {
            block.paint(g);
        });

        this.hrac.paint(g);
        this.ball.paint(g);

        if (!this.start) {
            g.setFont(new Font("Calibri", Font.BOLD, 35));
            g.setColor(Color.WHITE);
            g.drawString("Pressed enter to start a game", 20, 300);
        }

        g.setFont(new Font("Calibri", Font.BOLD, 20));
        g.setColor(Color.WHITE);
        g.drawString(String.valueOf(this.skore), 450, 540);

        if (this.gameOver) {
            g.setFont(new Font("Calibri", Font.BOLD, 80));
            g.setColor(Color.WHITE);
            g.drawString("Game Over", 60, 300);
        } else if (this.blocks.isEmpty()) {
            this.gameOver = true;
            g.setFont(new Font("Calibri", Font.BOLD, 80));
            g.setColor(Color.WHITE);
            g.drawString("You won", 80, 300);
        }
    }

    private void update() {
        this.ball.pohyb();
        repaint();
    }

    @Override
    public void keyTyped(KeyEvent e) {
    }

    @Override
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_ENTER) {
            this.start = true;
        }
        this.hrac.pressed(e.getKeyCode());
    }

    @Override
    public void keyReleased(KeyEvent e) {
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (!this.gameOver && this.start) {
            this.update();
        }
    }
}

class Blocks {

    private final int x;
    private final int y;
    private final int width;
    private final int height;

    public Blocks(int a, int b, int w, int h) {
        this.x = a;
        this.y = b;
        this.width = w;
        this.height = h;
    }

    public void paint(Graphics g) {
        g.setColor(Color.pink);
        g.drawRect(this.x, this.y, this.width, this.height);
        g.fillRect(this.x, this.y, this.width, this.height);
    }

    public int getX() {
        return this.x;
    }

    public int getY() {
        return this.y;
    }

    public int getWidth() {
        return this.width;
    }

    public int getHeight() {
        return this.height;
    }

}

public class Paddle {

    private int x;
    private final int y;
    private final int width;
    private final int height;
    private final int posun;
    private final Breaker game;

    /**
     *
     * @param game
     */
    public Paddle(Breaker game) {
        this.x = 245 - (60/2);
        this.y = 540;
        this.width = 100;
        this.height = 15;
        this.posun = 10;
        this.game = game;
    }

    public int getWidth() {
        return this.width;
    }

    public int getX() {
        return this.x;
    }

    public int getY() {
        return this.y;
    }

    public void paint(Graphics g) {
        g.setColor(Color.CYAN);
        g.drawRect(this.x, this.y, this.width, this.height);
        g.fillRect(this.x, this.y, this.width, this.height);
    }

    void pressed(int keyCode) {
        this.kontrola();
        if (keyCode == KeyEvent.VK_RIGHT) {
            this.x += this.posun;
        } else if (keyCode == KeyEvent.VK_LEFT) {
            this.x -= this.posun;
        }
    }

    private void kontrola() {
        if ((this.x + this.width) >= 490) {
            this.x = 490 - this.width;
        } else if (this.x <= 0) {
            this.x = 0;
        }
    }

}

public class Ball {

    private final int PRIEMER = 20;
    private int x;
    private int y;
    private int dx;
    private int dy;
    private final Breaker game;

    /**
     *
     * @param game
     */
    public Ball(Breaker game) {
        this.x = 245 - (PRIEMER / 2);
        this.y = 500;
        this.dx = 2;
        this.dy = 2;
        this.game = game;
    }

    void paint(Graphics g) {
        g.setColor(Color.PINK);
        g.drawOval(this.x, this.y, PRIEMER, PRIEMER);
        g.fillOval(this.x, this.y, PRIEMER, PRIEMER);
    }

    private void skontrolujKoliziu() {
        boolean kolizia = false;
        if (((this.y+PRIEMER) >= this.game.getPanel().getHrac().getY()) && ((this.x) >= this.game.getPanel().getHrac().getX()-2) && ((this.x+PRIEMER) <= (this.game.getPanel().getHrac().getX()+this.game.getPanel().getHrac().getWidth()+2))) {
            kolizia = true;
        } else if ((this.y + PRIEMER) >= 560) {
            this.game.getPanel().jeKoniec();
            return;
        }

        for (Blocks block : this.game.getPanel().getBlocks()) {
            if ((this.y+5 <= (block.getY() + block.getHeight())) && (this.x+5 >= block.getX()) && ((this.x+PRIEMER-5) <= block.getX()+block.getWidth())) {
                this.game.getPanel().getBlocks().remove(block);
                kolizia = true;
                this.game.getPanel().pridajBod();
                break;
            }
        }

        if (kolizia) {
            this.dy = - this.dy;
        }
    }
    public void pohyb() {
        if (this.x <= 0 || this.x >= (460 + PRIEMER)) {
            this.dx = -this.dx;
        } else if (this.y <= 0 || this.y >= (540 + PRIEMER)) {
            this.dy = -this.dy;
        }

        this.skontrolujKoliziu();

        this.x += this.dx;
        this.y += this.dy;
    }

}
Nahoru Odpovědět
22.8.2020 14:27
Neaktivní uživatelský účet
Avatar
Lubor Pešek
Člen
Avatar
Lubor Pešek:24.8.2020 15:32

:) šikulka. Sice ti to trošku uhýbá, kdy se kulička neodráží vpravo od zdi, ale projde zdí (protože tam neodečítáš při pravém odrazu její šířku), ale vypadá to zajímavě.

Musel jsem kód sice hodně upravit, abych to spustil, ale jinak dobrý start. Projdu si to a když tak ti napíšu, kudy se dál ubírat.

Nahoru Odpovědět
24.8.2020 15:32
Existují dva způsoby, jak vyřešit problém. Za prvé vyhoďte počítač z okna. Za druhé vyhoďte okna z počítače.
Avatar
Lubor Pešek
Člen
Avatar
Lubor Pešek:24.8.2020 16:07

Třída Breaker:

  • První, co mě hodně bouchlo do očí, tak je ten český název metody. Proč? Vždyť máš většinu v angličtině. Tak metody piš také anglicky.
  • Trošku zkus přemýšlet nad implementací. U takového jednoduchého projektu to není tak zásadní, ale vidím, že jsi holka šikovná, tak tě chci navnadit, aby ses už učila přemýšlet nad samotnou implementací. Myslím, že se ti za chvíli otevřou dveře pro webové technologie, tak proto tento bodík.

V main metodě deklaruješ proměnnou Breaker b. Toto je velice špatný návyk a pokud možno nezvykej si na to. Nauč se třeba ze začátku proměnné pojmenovávat stejně jako třídu, akorát s malým písmenem. Proměnná b je velmi špatný název a to zejména, jedná-li se o objekt!
Dále je to samotná deklarace. Využíváš ji tady pouze pro provolání jedné metody. Zkus uvažovat tak, že když vytváříš proměnnou, tak to není jen uložiště hodnot, ale využití místa v paměti.
Těch pár bajtíků teď skutečně nehraje roli a mám za to, že jedna proměnná ti nezpozdí runtime ani o milisekundu :D Nicméně zkus uvažovat tak, že budeš jednou pracovat s miliony dat. A tenhle zápis by už byl problém.
Proto pokud není doopravdy potřeba, tak nevytvářej proměnné.
Dalo by se to zapsat klidně i takhle:

public static void main(String[] args) {
        new Breaker().zobraz();
  }

A nejlepší by bylo, kdybys rovnou zavolala konstruktor. Přeci jen konstruktor je i od slova zkonstruovat, tak proč to nevyužít :)
Potom by celý příkaz vypadal jednoduše:

public static void main(String[] args) {
        new Breaker();
  }
  • Velice oceňuji využití přístupové metody pro atribut panel. To svědčí o tom, že jsi na dobré cestě stát se skvělou programátorkou;)
  • poslední poznámečku, kterou bych měl, tak je klíčové slovo this u panelu. V Javě to není potřeba, pokud nesetuješ parametr přes stejný název. Není to chyba a pokud bys přešla na JavaScript, tak tam to naopak povinné je :) zvlášť u angularu platí víc thisů víc adidas :D Ale tady se to dalo napsat jak v getteru tak v metodě zobraz() bez toho this.
  • U metody zobraz() nevidím kromě českého názvu problém. Pro swingovskou aplikaci ti poradím dvě fintičky, které jsou příjemné :)

Jde mi o zarovnání na střed obrazovky.
Poradím ti dva způsoby, z čehož je jeden speciálně pro swing a druhý, který můžeš využít i tu a tam v budoucnu kdekoliv jinde.
Java dokáže získat aktuální rozlišení obrazovky (ještě aby to neuměla, hlavně na desktopu musí umět všechno :) ).
To získáš pomocí třídy Dimension. Ale abys ji mohla použít, potřebuješ nástroj ze třídy Toolkit. Přiznám se, že já osobně jsem tuto třídu nepoužil nikdy na nic jiného. Takže já jsem se to prostě mechanicky naučil (není to úplně správný přístup, ale nebudu ti přece lhát).

Kdybys měla zájem, tak tyto třídy nabízí samozřejmě více možností. Kupříkladu dokáží i vytáhnout aktuální obrazovku (v podstatě printscreen). To jsem třeba používal, když jsem experimentoval a z prdele si zkoušel napsat virus :)

private final Toolkit tool = Toolkit.getDefaultToolkit();
private final Dimension src = tool.getScreenSize();

V konstantě src máš nyní uložené parametry aktuální obrazovky (aktuálního rozlišení).
No a nyní můžeš použít tento údaj pro výpočet.
Abys zarovnala něco na střed, tak musíš znát rozměr plochy, která slouží jako podklad a plochu, která se má zarovnat. Pro šířku i výšku to musíš nastavit samostatně.

Předvedu ti to na šířce.
Chceš, aby úsečka o rozměrech 20cm měla střed ve stejném bodě, jako úsečka 100cm
Toho docílíš tak, že potřebuješ 2 počáteční souřadnice. U první úsečky (100cm) je to samozřejmě 0. (Začínáme od nuly :) )
U druhé úsečky musíme tuto počáteční souřadnici vypočítat pomocí vzorečku:
šířka první úsečky / 2 - šířka druhé úsečky / 2
V tomto případě tedy druhá úsečka (20cm) bude začínat v bodě:
(100 / 2) - (20 / 10) Což je 50 - 10, takže druhá úsečka musí začínat v bodě 40.

Zkus si to třeba i nakreslit a uvidíš, že ti to dá lepší smysl :)

No a tak díky konstantně src, ve které máš parametry obrazovky a díky rozlišení framu, který taky znáš, tak to můžeš implementovat. Takže výsledný kód by vypadal následovně:

//src.width / 2 - frame.getWidth() / 2 <- to je pro x souřadnici
//src.height / 2 - frame.getHeight() / 2 <- to je pro y souřadnici
frame.setLocation(src.width / 2 - frame.getWidth() / 2, src.height / 2 - frame.getHeight() / 2);

Swing výše uvedený výpočet umí provést jednoduchou metodou. Takže stejného výsledku docílíš klidně i takhle:

frame.setLocationRelativeTo(null);

Parametr null znamená, že se bere defaultně obrazovka. Jinak bys tam mohla dát componentu swingu.

  • Poslední, co mě napadá, co bys mohla rozšířit, tak že bys hodnoty 490, 600 a "Block Breaker" nepsala v kódu, ale vytvořila si na ně třeba Properties. To si třeba nastuduj a zkus implementovat. Může to být hezký úvod pro práci se soubory.
Nahoru Odpovědět
24.8.2020 16:07
Existují dva způsoby, jak vyřešit problém. Za prvé vyhoďte počítač z okna. Za druhé vyhoďte okna z počítače.
Avatar
Lubor Pešek
Člen
Avatar
Lubor Pešek:24.8.2020 16:53

Třída BlockBreakerPanel:

  • tady se pár věcí najde:) předně ty magické hodnoty by šly odstranit. Magická hodnota je číslo, které máš na pevno v kódu. To znamená, že ti to bude teď opravdu fungovat. Ale když změní rozlišení framu hry, tak se ti to rozhodí. Takže určitě získávej hodnoty z jednoho místa. Jak jsem ti doporučoval tu třídu Properties, tak to můžeš získávat ze souboru. Ale nervy pak zas všechno přímo sem. Neboj se mít víc tříd. A jednu třídu specializuj pouze na čtení ze souboru. A pak veškeré hodnoty ber pouze z jednoho místa. To je nejžádanější. Mít konfiguraci hezky pohromadě. U složitějších projektů se to potom sice dělí, ale to hlavně z důvodu bezpečnosti. Ale podívej se - ty už tady v lehkém projektíku nastavuješ velikost hry v jedné třídě a velikost kostiček ve druhé. A teď si představ, že budeš mít složitou aplikaci a každý výpočet bude někde jinde... To by tě jeblo. Zkus mít logiku výpočtu hezky na jednom místě. Třeba v jednom balíčku (nazvi ho třeba businesslogic :) ) a tam měj třeba 1 nebo 3 třídy, kde budeš mít různé výpočty.
  • s předchozím bodem souvisí i celá tato třída. Když mi řekneš JPanel, tak očekávám grafický objekt případně jeho vlastnosti a funkce. Tys do této třídy narvala veškerou logiku hry. Výpočty pohybu, posluchače, další objekty, atd.

Toto je velká chyba proti OOP a zejmena proti dědičnosti.

Ano, ono to jde a funguje to. Ale o tom vlastně celé programování je. Nemůžeš ťukat všechno tak, že to prostě jde. Ale musíš to rozdělovat do logických celků. Potom i sama poznáš, že programuješ správně - tak, že si to dokážeš logicky zdůvodnit, proč je tohle tam a tohle tam.

Hlavně se neboj více specifikovat skupiny. Rozděl určitě grafiku a logiku hry. Logiku rozděl tak, že jedna část se bude starat jen o výpočty komponent, další jen o skore a třeba o vyhodnocování hry (jestli jsi prohrála či ještě ne) a další logika třeba zas na něco jiného.

V podstatě... Kdybych ti teď řekl - zkus naprogramovat ukládání hry, tak ty to budeš hledat v panelu... To nedává smysl.

Dám ti pár rad, čím se řídit:

  • když naprogramuješ obejkt, nejdřív zkus, jestli jej můžeš použít i jinde. Například. Naprogramuješ balonek. Tak zkus třídu balonek vyjmout z projektu, vytvoř úplně nový projekt a zjisti, co všechno musíš mít, abys tento balonek použila v tom novém projektu. Tím si ověříš, že programuješ objektově. Jestli je tvůj balonek závislý na nějakém jiném objektu, je to chyba. Pokud máš třeba nějakou swing komponentu, tak by ti mělo stačit, že ji přidáš na libovolný frame nebo panel. Nic víc nepotřebuješ pokud ano, už je to nějak specializované a je to chybka.

Příklad s tím balonkem. Měla by to být taková komponenta, kterou implementuješ do jiného panelu a on se bude stejně pohybovat, bude stejně reagovat a bude mít stejné možnosti.
Ať to dáš na JPanel, JFrame nebo třeba JTabbedPane.

  • další věc dědičnost. Dědičnost by měla rozšiřovat předka, ale neměla by drasticky změnit jeho podstatu. Povím ti příklad z praxe.

Úloha zněla. Mám žábu a cyklistu. Oba mají definované metody pro pohyb. Žába skáče, cyklista jede na kole po silnici.
Problém:
Máme úsek bez silnice. Místo silnice je voda a kameny. Jaké je řešení problému pro cyklistu, který nemůže projet?
Jedním z návrhů bylo, že cyklista bude dědit od žáby, takže mohl přeskákat s kolem po kamenech.

Fungovalo to! Bylo to super.
Sranda byla, že cyklista zdědil všechny metody žáby. Takže najednou uměl skvěle kvákat a klást vajíčka...

Nejde jen o to, že nějaké metody použiješ a nějaké prostě ne. Projekt by měl mít logiku.

Ve tvém podání, kdybych si vzal třídu BlockBreakerPanel
(Kdyby se mi třeba líbilo, že by tento panel měl krásné duhové pozadí), tak díky tomu získám i většinu logiky celé hry, ale ta mi nebude někdy fungovat, protože potřebuje další třídy, se kterými nepočítám...

To máš stejné, jako kdyby sis koupila auto a k němu by sis musela koupit i globus, protože špatný výrobce do auta naprogramoval, i tlačítko, kterým rozsvěcuješ globus. Asi by se ti to nelíbilo, protože od auta bys v prvé řadě očekávala, že umí jezdit a ne nějaký globus.

Nahoru Odpovědět
24.8.2020 16:53
Existují dva způsoby, jak vyřešit problém. Za prvé vyhoďte počítač z okna. Za druhé vyhoďte okna z počítače.
Avatar
Lubor Pešek
Člen
Avatar
Lubor Pešek:25.8.2020 7:34

Promiň, včera jsem měl ještě povinnosti, tak jsem to musel utnout.
Další věc, co bych tam ještě viděl, tak si nastuduj něco o rozhraní a datových typech (takhle dohromady, kdy rozhraní figuruje jako datový typ).
Narážím na deklaraci a implementaci proměnné

private ArrayList<Blocks> blocks = new ArrayList<>();

Je lepší v takovýchto případech deklarovat datový typ obecný. Tím myslím, že bys mohla tento kód nahradit tímto

private List<Blocks> blocks = new ArrayList<>();

Je to dobré proto, protože kdyby ses rozhodla někdy, že ti nevyhovuje ArrayList, ale třeba LinkedHashList, tak ti stačí změnit už pouze konstruktor v implementaci. No a bude ti to vždy takhle sedět.

Nahoru Odpovědět
25.8.2020 7:34
Existují dva způsoby, jak vyřešit problém. Za prvé vyhoďte počítač z okna. Za druhé vyhoďte okna z počítače.
Avatar
Lubor Pešek
Člen
Avatar
Lubor Pešek:25.8.2020 8:05

Určitě bych pro popisek nepoužíval grafickou podobu nástroje. Jednak to vypadá, jako text v DOS a hlavně proč by popisek nemohl být samostatnou komponentou? Především, když to swing tak krásně nabízí - třída Label.
Takže určitě tohle bys mohla přehodnotit.
Minimálně i proto, kdybys chtěla implementovat pauzu nebo různě editovat text (třeba i s ním pracovat), tak tím, že jsi odkázána pro překreslování všeho, tak je to chyba.

Dále jsem trošku nepochopil ten for-each, kterým provoláváš metody paint pro jednotlivé blocky. Je tam potřeba? Když třídě Blocks (mimochodem nepojmenovávej třídu množným číslem. Když vidím proměnnou blocks, tak mě první napadne, že je to nějaká kolekce nebo pole Blocků) nastavíš dědičnost z JComponent (vytvoříš z ní componentu), tak při její vytvoření se vždy provolá třída paint. (respektive repaint() ).

Takže ten for-each nepotřebuješ kór když bys chtěla provolávat tuhle metodu.
Aby to ovšem fungovalo, musí tedy Blocks dědit z JComponent a potom je musíš i přidat na ten panel.

Pak koukám ještě na dvě věci.

Když jsem si to procházel, tak myslím, že by sis mohla zjistit také, jako swing funguje. Ono je to jednoduché - je to skládání komponent na panel a panely na sebe.
Ty máš veškerou kreslící logiku v jedné metodě. To nesouvisí s tím, co jsem ti říkal o tom, že by takováto logika měla být na jednom místě. Prioritně každý objekt by měl umět stát sám o sobě. Balonek by se měl umět vykreslit naprosto nezávisle na ostatních komponentách. To samé Kostka to samé plošina to samé hlavní okno aplikace.
Společná logika na jednom místě tím myslím výpočty případně různé parametry či jazykové mutace atd.

Lupnu ti sem i takový jednoduchý modelový příklad, ze kterého se můžeš inspirovat.

Nahoru Odpovědět
25.8.2020 8:05
Existují dva způsoby, jak vyřešit problém. Za prvé vyhoďte počítač z okna. Za druhé vyhoďte okna z počítače.
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
Lubor Pešek
Člen
Avatar
Lubor Pešek:25.8.2020 8:12

No a poslední věc, co bych asi viděl v této třídě, tak jsou posluchače kláves.
Stejně jako u mouseListenerů, tak i keyListener správně implementuješ pomocí rozhraní KeyListener.
Jak KeyListener, tak MouseListener mají v sobě několik metod. Ale sama jsi teď ukázala, že ne vždy všechny využiješ.
U těchto dvou rozhraní se vývojářům zželelo nás programátorů, abychom nemuseli implementovat prázdné metody a tak pro tato dvě rozhraní implementovali do standardní knihovny i jejich abstraktní podoby.

To souvisí s tématem Rozhraní vs Abstraktní třída - to si klidně nastuduj;)
V praxi to pro tebe bude znamenat tohle:
Máš rozhraní MouseListener. Toto rozhraní implementuje metody:

  • mouseClicked
  • mousePressed
  • mouseReleased
  • mouseEntered
  • mouseExited

Ty bys potřebovala jen mousePressed metodu
Tak abys neměla potom zbytečně další 4 metody, které by byly prázdné, tak můžeš místo rozhraní použít abstraktní třídu MouseAdapter

a díky tomu si můžeš vybrat přímo metodu/metody, které chceš použít.

Jinak není potřeba implementovat rozhraní. Ukážu ti v dalším příkladu, jak používat ve swingu key a mouse listnery/adaptery a ukážu ti i na tom konkrétní rozdíl.

Nahoru Odpovědět
25.8.2020 8:12
Existují dva způsoby, jak vyřešit problém. Za prvé vyhoďte počítač z okna. Za druhé vyhoďte okna z počítače.
Avatar
Lubor Pešek
Člen
Avatar
Lubor Pešek:25.8.2020 8:51
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.*;

public class Frame extends JFrame {

  private int widthOfFrame = 500;
  private int heightOfFrame = 500;
  private Color ballColor = Color.RED;
  private int ballSize = 25;
  private int movingSpeed = 5;

  public Frame() {
    // 1. nastavíš si hlavní okno (můžeš použít dědičnost z JFrame a nebo JFrame skládat pomocí atributů, to je na tobě)
    // NBD (Nice to Be Done) následujcí 4 řádky bys klidně mohla hodit samostatně třeba do metody init().
    setSize(widthOfFrame + 6, heightOfFrame + 40);
    setLocationRelativeTo(null);
    setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    setVisible(true);

    // 2. nastavíš si nějaký panel (klidně to může být tedy i tvoje vlastní třída, ale pozor, pokud dědíš z panelu, tak by podstata třídy měla zůstat panelem a nic jiného!!)
    JPanel panel = new JPanel();
    add(panel); // a panel přidáš hlavnímu oknu. Tím to končí
    panel.setLayout(null); // Pokud budeš chtít programovat vykreslovací program, je dobré nenastavovat žádný layout. Díky tomu můžeš krásně kreslit bez toho, aniž by ti to automaticky někde zarovnávalo komponenty
    // pak mu tady můžeš klidně nastavovat barvičky podle libosti

    // 3. přidávej si kompoenty na panel (případně to můžeš opět víc rozčlenit na více panelů - jeden panel na skore, jeden na hru, jeden na menu, atd. atd. Swing je skutečně o skládání panelů. Proto existuje i mnoho layoutů)
    Ball ball = new Ball(ballColor, ballSize);
    panel.add(ball);
    ball.moveBall(movingSpeed, heightOfFrame);

    // následující dva bloky (body 4 a 5) budou dělat úplně to samé!!!
    // 4. mouseListener (pomocí listner)
    panel.addMouseListener(new MouseListener() {
      @Override
      public void mouseClicked(MouseEvent e) {

      }

      @Override
      public void mousePressed(MouseEvent e) {
//        Ball ball = new Ball(ballColor, ballSize);
//        ball.setLocation(e.getX() - ball.getWidth() / 2, e.getY() - ball.getHeight() / 2);
//        panel.add(ball);
//        ball.moveBall(movingSpeed, heightOfFrame);
      }

      @Override
      public void mouseReleased(MouseEvent e) {

      }

      @Override
      public void mouseEntered(MouseEvent e) {

      }

      @Override
      public void mouseExited(MouseEvent e) {

      }
    });

    // 5. mouseAdapter (abys viděla rozdíl)
    panel.addMouseListener(new MouseAdapter() {
      @Override
      public void mousePressed(MouseEvent e) {
        Ball ball = new Ball(ballColor, ballSize);
        ball.setLocation(e.getX() - ball.getWidth() / 2, e.getY() - ball.getHeight() / 2);
        panel.add(ball);
        ball.moveBall(movingSpeed, heightOfFrame);
      }

      @Override
      public void mouseReleased(MouseEvent e) {
        System.out.println("Kliklo se"); // tato metoda je totálně na hovno:) jen je to ukázka toho, že si můžeš skutečně vybrat tolik metod, kolik potřebuješ
      }
    });

    // To samé platí i pro keyListenera. Také můžeš vytvořit nový keyAdapater a vybrat si některou z těch metod

  }

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

Kdybys měla jakékoliv dotazy, klidně mi i napiš soukromě. Vidíš sama, jaký je to už jen ode mě spam :D ještě pár věcí by se našlo, ale myslím, že to už teď stačí :)

PS: tady máš případné vylepšení té blbinky, co jsem ti poslal, jen pro zábavu :D

panel.addMouseMotionListener(new MouseAdapter() {
      @Override
      public void mouseMoved(MouseEvent e) {
        Ball ball = new Ball(ballColor, ballSize);
        ball.setLocation(e.getX() - ball.getWidth() / 2, e.getY() - ball.getHeight() / 2);
        panel.add(ball);
        ball.moveBall(movingSpeed, heightOfFrame);
      }
    });
Editováno 25.8.2020 8:53
Nahoru Odpovědět
25.8.2020 8:51
Existují dva způsoby, jak vyřešit problém. Za prvé vyhoďte počítač z okna. Za druhé vyhoďte okna z počítače.
Avatar
Radek Veverka
Redaktor
Avatar
Odpovídá na Lubor Pešek
Radek Veverka:25.8.2020 17:06

Proto pokud není doopravdy potřeba, tak nevytvářej proměnné.

To bych neřekl. Klidně lokální proměnné vytvářej, pokud myslíš, že je pak kód přehlednější. Mnohdy je lepší si rozepsat kód na více řádků a pracovat s dobře pojmenovanými dočasnými proměnnými, než vše nacpat na jeden 200 znakovej řádek. A co se týče výkonu, optimalizátor se ti o to většinou automaticky postará a vnitřně si kód předělá. S lokálními proměnnými bych si opravdu nedělal starosti. Třeba v C je to trochu o něčem jiném, ale v Javě je IMHO irelevantní nad tím takto úzkostlivě uvažovat.

 
Nahoru Odpovědět
25.8.2020 17:06
Avatar
Lubor Pešek
Člen
Avatar
Odpovídá na Radek Veverka
Lubor Pešek:25.8.2020 18:25

Když vezmeš v potaz webové služby, tak tam záleží při velkém objemu dat na každé milisekundě. Ano, určitě není přehledné narvat logické výrazy všechny do jednoho řádku. Ale vytvářet proměnnou pouze pro jeden účel, do kterého nedáš ani výpočet, tak to fakt nemá smysl.

Nahoru Odpovědět
25.8.2020 18:25
Existují dva způsoby, jak vyřešit problém. Za prvé vyhoďte počítač z okna. Za druhé vyhoďte okna z počítače.
Avatar
Radek Veverka
Redaktor
Avatar
Odpovídá na Lubor Pešek
Radek Veverka:25.8.2020 20:05

Souhlasím, reagoval jsem ale na tu konkrétní citovanou větou. Pointa je neprasit si kód tím, že budu pořád myslet na to, abych měl co nejméně lokálních proměnných. Mám optimalizátor. Navíc aplikace zpravidla spálí 90% výkonu na 10% kódu, takže když už, tak to stačí řešit pouze v těchto kritických místech.

 
Nahoru Odpovědět
25.8.2020 20:05
Avatar
Odpovídá na Lubor Pešek
Neaktivní uživatel:26.8.2020 20:08

Ahoj,

v prvom rade ti chcem veľmi poďakovať za reakciu a pomoc. Skúšam to prerobiť podľa tvojich postrehov, no vôbec to nie je jednoduché. Napríklad by ma zaujímalo čo s ActionListenerom, potrebujem ho v podstate na prekreslovanie tých komponentov pomocou metódy repaint()? Môže byť napísané obdobne ako mouseListener?

Nahoru Odpovědět
26.8.2020 20:08
Neaktivní uživatelský účet
Avatar
Lubor Pešek
Člen
Avatar
Odpovídá na Neaktivní uživatel
Lubor Pešek:26.8.2020 23:06

Actionlistener nepotřebuje takovou implementaci, protože obsahuje pouze jedinou metodu - actionPerform. Takže pokud toto použiješ, tak vždy pro tuto metodu.
Tento posluchač se využívá u tlačítek.

Nahoru Odpovědět
26.8.2020 23:06
Existují dva způsoby, jak vyřešit problém. Za prvé vyhoďte počítač z okna. Za druhé vyhoďte okna z počítače.
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 14 zpráv z 14.