10. díl - Programování jednoduchých Java GUI her - Detekce kolizí 2

Java Tvorba her Programování jednoduchých Java GUI her - Detekce kolizí 2

Pokračování předchozí lekce.

Vytvoříme si třídu Ctverec.

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.util.Random;

public class Ctverec {

    private final int DELKA_STRANY = 50;
    private int x, y;
    private Color barva;

    public Ctverec(int x, int y) {
        this.x = x;
        this.y = y;

        vyberBarvu();
    }

    public void vykresliSe(Graphics g) {
        g.setColor(barva);
        g.drawRect(x, y, DELKA_STRANY, DELKA_STRANY);
    }

    private void vyberBarvu() {
        Random generator = new Random();
        int nahodneCislo = generator.nextInt(4);

        switch (nahodneCislo) {
            case 0:
                barva = Color.blue;
                break;
            case 1:
                barva = Color.red;
                break;
            case 2:
                barva = Color.yellow;
                break;
            default:
                barva = Color.green;
                break;
        }
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public Color getBarva() {
        return barva;
    }

    public Rectangle getOkraje() {
        return new Rectangle(x, y, DELKA_STRANY, DELKA_STRANY);
    }
}

 

private final int DELKA_STRANY = 50;

Námi vytvořené čtverce budou veliké 50px.

private int x, y;
private Color barva;

Deklarujeme proměnné x, y, ve kterých bude uložna aktuální pozice. V proměnné barva bude barva čtverce.

public void vykresliSe(Graphics g) {
    g.setColor(barva);
    g.drawRect(x, y, DELKA_STRANY, DELKA_STRANY);
}

Metodě vykresliSe() objektu Ctverec se předává grafický kontext a na něj se vykreslí aktuální Ctverec.

private void vyberBarvu() {
    Random generator = new Random();
    int nahodneCislo = generator.nextInt(4);

    switch (nahodneCislo) {
        case 0:
            barva = Color.blue;
            break;
        case 1:
            barva = Color.red;
            break;
        case 2:
            barva = Color.yellow;
            break;
        default:
            barva = Color.green;
            break;
    }
}

Metoda vyberBarvu() nastaví hodnotu proměnné barva na náhodně vybranou barvu (vybírá se ze čtyř barev).

public Rectangle getOkraje() {
    return new Rectangle(x, y, DELKA_STRANY, DELKA_STRANY);
}

Metoda getOkraje() vrací oblast, na které se nachází aktuální objekt Ctverec.

Dále si vytvoříme třídu Chytac. Je podobná té z předchozího příkladu, přibyla metoda getOkraje(), která stejně vrací oblast, kde se nachází objekt Chytac. Počáteční hodnota x, y je stanovena napevno a lehce se změnila metoda move().

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

public class Chytac implements KeyListener {

    private Color barva = Color.white;
    private final int SIRKA = 50;
    private final int VYSKA = 10;
    private int x, y, smerX, smerY, rychlost;
    private MujPanel panel;

    public Chytac(MujPanel panel) {

        this.rychlost = 3;
        this.panel = panel;
        this.x = 175;
        this.y = 150;
    }

    public void vykresliSe(Graphics g) {
        g.setColor(barva);
        g.drawRect(x, y, SIRKA, VYSKA);
        System.out.println(panel.getWidth());
    }

    public void move() {
        x += smerX;
        y += smerY;

        if ((x >= panel.getSIRKA_PANELU() - (SIRKA + 1)) && (panel.getSIRKA_PANELU() > 0)) {
            x = panel.getSIRKA_PANELU() - (SIRKA + 1);
        }
        if (x <= 0) {
            x = 0;
        }
        if ((y >= panel.getVYSKA_PANELU() - (VYSKA + 1)) && (panel.getVYSKA_PANELU() >0)) {
            y = panel.getVYSKA_PANELU() - (VYSKA + 1);
        }
        if (y <= 0) {
            y = 0;
        }
    }

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

    }

    public Rectangle getOkraje() {
        return new Rectangle(x, y, SIRKA, VYSKA);
    }
}

Následuje třída MujPanel.

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.util.Random;
import javax.swing.JPanel;
import javax.swing.Timer;

public class MujPanel extends JPanel {

    private final int SIRKA_CTVERCE = 400;
    private final int VYSKA_CTVERCE = 300;
    private final int POCET_CTVERCU = 10;
    private Ctverec[] poleCtvercu;
    private Chytac chytac;

    public MujPanel() {
        this.poleCtvercu = new Ctverec[POCET_CTVERCU];
        this.chytac = new Chytac(this);

        this.setPreferredSize(new Dimension(SIRKA_CTVERCE, VYSKA_CTVERCE));
        this.setBackground(Color.black);

        this.addKeyListener(chytac);
        this.setFocusable(true);

        Random generator = new Random();
        for (int i = 0; i < poleCtvercu.length; i++) {
            int x = generator.nextInt(SIRKA_CTVERCE - 50);
            int y = generator.nextInt(VYSKA_CTVERCE - 50);

            Ctverec c = new Ctverec(x, y);
            poleCtvercu[i] = c;
        }

        Timer casovac = new Timer(10, new PosluchacCasovace());
        casovac.start();
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        for (int i = 0; i < poleCtvercu.length; i++) {
            if (poleCtvercu[i] != null) {
                poleCtvercu[i].vykresliSe(g);
            }
        }

        chytac.vykresliSe(g);
    }

    private class PosluchacCasovace implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {

            chytac.move();

            for (int i = 0; i < poleCtvercu.length; i++) {
                if (poleCtvercu[i] != null) {
                    // if (chytac.getOkraje().intersects(poleCtvercu[i].getOkraje()))
                    Rectangle okrajeChytace = chytac.getOkraje();
                    Rectangle okrajeCtverce = poleCtvercu[i].getOkraje();
                    if (okrajeChytace.intersects(okrajeCtverce)) {
                        poleCtvercu[i] = null;
                    }
                }
            }

            repaint();
        }
    }

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

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

 

Random generator = new Random();
for (int i = 0; i < poleCtvercu.length; i++) {
    int x = generator.nextInt(SIRKA_CTVERCE - 50);
    int y = generator.nextInt(VYSKA_CTVERCE - 50);

    Ctverec c = new Ctverec(x, y);
    poleCtvercu[i] = c;
}

V cyklu vytvářím objekty typu Cverec a referenci na ně ukládám do pole poleCtvercu.

@Override
public void paintComponent(Graphics g) {
    super.paintComponent(g);

    for (int i = 0; i < poleCtvercu.length; i++) {
        if (poleCtvercu[i] != null) {
            poleCtvercu[i].vykresliSe(g);
        }
    }

    chytac.vykresliSe(g);
}

V metodě paintComponent() nejdříve volám rodičovskou metodu, která mi vykreslí pozadí. Pak procházím polem, ve kterém mám uloženy objekty typu Ctverec. Pokud je na daném indexu Ctverec (index není null), zavolám jeho metodu vykresliSe a předám jí grafický kontext. Výsledkem je, že každý Ctverec v poli dostane grafický kontext, na který se vykreslí. Nakonec volám kreslící metodu objektu Chytac.

private class PosluchacCasovace implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {

        chytac.move();

        for (int i = 0; i < poleCtvercu.length; i++) {
            if (poleCtvercu[i] != null) {
                // if (chytac.getOkraje().intersects(poleCtvercu[i].getOkraje()))
                Rectangle okrajeChytace = chytac.getOkraje();
                Rectangle okrajeCtverce = poleCtvercu[i].getOkraje();
                if (okrajeChytace.intersects(okrajeCtverce)) {
                    poleCtvercu[i] = null;
                }
            }
        }

        repaint();
    }
}

Při každé události ActionEvent, která je vygenerovaná objektem Timer, se volá metoda actionPerformed objektu PosluchacCasovace. Ta nejdříve zavolá metodu move() objektu Chytac, která provede změny v pozici chytace. Pak v cyklu procházím polem s uloženými objekty Ctverec a kontoluji, zda se okraje daného Ctverce nepřekrývají s okraji chytace. Pokud ano, index, na kterém se nachází tento ctverec, změním na null. Tím pádem Ctverec pro mne přestává existovat.

Spouštěcí třída s hlavním oknem programu zůstala nezměněna.

Detekování kolizí objektů v jednoduché Java hře

 

Stáhnout

Staženo 257x (3.08 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 (2 hlasů) :
4.54.54.54.54.5


 



 

 

Komentáře

Avatar
Iwitrag
Člen
Avatar
Iwitrag:

Moc dobrý tutorial :) Zkusil jsem si udělat kolize, že se chytač zastaví pokud narazí na čtverec...
kontrolu kolize jsem dělal přímo v metodě move() chytače, která před pohybem kontrolovala, jestli se chytač posunutý o smerX a smerY neprotíná s nějakým se čtverců, pokud se protíná, tak pohyb v dané ose (X nebo Y) nevykoná...

Akorát jsem musel předat referenci na všechny čtverce i chytačovi a celkově mi to přišlo strašně zdlouhavé a přemrštěné... určitě by se to dalo udělat i jednodušeji, tak nad tím teď přemýšlím :)

Navíc vznikl další problém - když se chytač zastavil, tak se mohlo stát, že mezi nim a čtvercem zůstala mezera (třeba 2 pixely), takže jsem musel v cyklu vytvářet nové "masky" chytače posunuté o rychlost-1 a rychlost-2 pixelů daným směrem (přes cyklus) a případně chytače "doposunout" aby byl těsně u čtverce, se kterým koliduje...

Ale ještě jednou moc díky :) Cítím ten progress a čím dál víc věcí, které jsem ze začátku nechápal, mi začalo dávat smysl :)

Odpovědět 19.1.2014 19:15
Učím se ostře vidět.
Avatar
vita
Redaktor
Avatar
Odpovídá na Iwitrag
vita:

Ahoj, tutoriál není dokonalý, ale jsem rád, že ti pomohl.

 
Odpovědět 6.2.2014 20:43
Avatar
Odpovídá na vita
Michal Žůrek (misaz):

Dokonalý bude až to všechno popíšeš 9/10 tutorialu je jenom kód, což je nezajímavé a hůř se z toho učí. Nezkoumal jsem ostatní, ale tímto stylem by jsi mohl všech 10 dílů sloučit do 2-4 dílů.

Odpovědět  ±0 6.2.2014 21:56
Nesnáším {}, proto se jim vyhýbám.
Avatar
zikako
Člen
Avatar
zikako:

No tutorial je docela dobrý..
Ale hodilo by se jich tu mnohem více.
Jako v C# sekci tam jsou na hry úžasné:
Třeba nějaké tutoriály na lwjgl a podobné

Odpovědět 21.2.2014 16:17
pospile = enemy :-)
Avatar
jozefekrcho
Člen
Avatar
jozefekrcho:

Da sa nieco podobne spravit aj pre ine tvary ako Rectangle ? narp kolizia kruhov ? a ak ano akoju mam definovat lebo som skusal RoundRectangle2D ale neviem to vratit ako navratovu hodnotu aby som porovnal okraje ...

 
Odpovědět 13.3.2014 20:44
Avatar
Jiří Gracík
Redaktor
Avatar
Odpovídá na jozefekrcho
Jiří Gracík:

U kruhů se spočítá délka vektoru mezi středy kruhů. Pokud je menší, než součet poloměrů obou kruhů, tak spolu kolidují. U nepravidelných tvarů se nejprve udělá rectangle okolo tvarů, porovná se kolize mezi nimi a když kolidují, tak se počítají kolize per pixel. Další možná poradí něco víc.

Odpovědět 13.3.2014 21:27
Creating websites is awesome till you see the result in another browser ...
Avatar
jozefekrcho
Člen
Avatar
Odpovídá na Jiří Gracík
jozefekrcho:

Mna by hlavne zaujmalo akym povelom ten kruh vytvorim aby som to mohol vraciat v metode lebo je napr drawOval ale nemozem pouzit public Oval dajOkraje() {}... to nejde Oval nevie co je a RoundRectangle tiez nejde jebe ma to neviem ako to mam spravit pomenovat ... keby nieko nieco s tym poradil alebo neaky link na stranku kde je zoznam tvarov co sa daju pouzivat ako Rectangle by som moc ocenil :)

 
Odpovědět 14.3.2014 1:41
Avatar
1Pupik1989
Člen
Avatar
Odpovídá na jozefekrcho
1Pupik1989:

Dá se to vyřešit, když vytvoříš ještě jeden objekt "CollisionDetec­tion" (tak jsem ho pojmenoval já. A pak jen stačí vytvořit metodu "collide", kde podle vstupních parametrů budeš řešit vztahy a volat podle toho třídy objektů.

public class CollisionDetection{
  public static void main(String[] args) {

  }

  public static boolean collide(Rectangle rect, Ellipse ellipse){
    //zjištění kolize mezi čtyřúhelníkem a elipsou
  }

  public static boolean collide(Rectangle rect, Polygon polygon){
    //zjištění kolize mezi čtyřúhelníkem a mnohoúhelníkem
  }
}

Doufám, že jsem tam nenaplácal nějaký nesmysl. Javu už jsem nějaký pátek neviděl. :D

 
Odpovědět  +1 14.3.2014 8:58
Avatar
Ladislav Ondris:

Super tutorial :)

Podle mě lepší než u OOP, protože vidím výsledný kód. V OOP jsem často nevěděl, co kam dopsat, byl v tom zmatek, takže jsem si musel stáhnout výslednou práci a zkopírovat :P

ale i tak jsou tyto všechno tutorialy super, klaním se vám, co jste to sepsali, moc nám to pomáhá :)

Odpovědět 30.4.2014 23:54
Pokud neděláš chyby, nepracuješ na dostatečně těžkých problémech.
Avatar
Ladislav Ondris:

Pokud byste chtěl, aby se váš chytač zastavil o vygenerované čtverce, tak stačí takto poupravit PosluchacCasovace (viz níže) a vytvořit nějaký ten getter a setter ve tříde chytac :)

private class PosluchacCasovace implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        a = chytac.getX();
        b = chytac.getY();
        chytac.move();

        for (int i = 0; i < getPoleCtvercu().length; i++) {
                // if (chytac.getOkraje().intersects(poleCtvercu[i].getOkraje()))
                Rectangle okrajeChytace = chytac.getOkraje();
                Rectangle okrajeCtverce = getPoleCtvercu()[i].getOkraje();
                if (okrajeChytace.intersects(okrajeCtverce)) {
                        chytac.setX(a);
                        chytac.setY(b);
                }
        }
        repaint();
    }
}
Odpovědět 1.5.2014 20:58
Pokud neděláš chyby, nepracuješ na dostatečně těžkých problémech.
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 10.