6. díl - Java - Aréna s bojovníky

Java Objektově orientované programování Java - Aréna s bojovníky

V minulém tutoriálu jsme si vytvořili třídu bojovníka. Hrací kostku máme hotovou z prvních lekcí objektově orientovaného programování. Dnes tedy dáme vše dohromady a vytvoříme funkční arénu. Tutoriál bude spíše oddechový a pomůže nám zopakovat si práci s objekty.

Potřebujeme napsat nějaký kód pro obsluhu bojovníků a výpis zpráv uživateli. Samozřejmě ho nebudeme bušit rovnou do výchozího souboru s metodou main(), ale vytvoříme si objekt Arena, kde se bude zápas odehrávat. V main se() potom jen založí objekty a o zbytek se bude starat objekt Arena. Přidejme k projektu tedy poslední třídu a to Arena.java

Třída bude víceméně jednoduchá, jako atributy bude obsahovat 3 potřebné instance: 2 bojovníky a hrací kostku. V konstruktoru se tyto atributy naplní z parametrů. Kód třídy bude tedy následující (komentáře si dopiště):

class Arena
{
        private Bojovnik bojovnik1;
        private Bojovnik bojovnik2;
        private Kostka kostka;

        public Arena(Bojovnik bojovnik1, Bojovnik bojovnik2, Kostka kostka)
        {
                this.bojovnik1 = bojovnik1;
                this.bojovnik2 = bojovnik2;
                this.kostka = kostka;
        }

}

Zamysleme se nad metodami. Z veřejných metod bude určitě potřeba jen ta k simulaci zápasu. Výstup programu na konzoli uděláme trochu na úrovni a také umožníme třídě Arena, aby přímo ke konzoli přistupovala. Rozhodli jsme se, že výpis bude v kompetenci třídy, jelikož se nám to zde vyplatí. Naopak kdyby výpis prováděli i bojovníci, bylo by to na škodu (nebyli by univerzální). Potřebujeme tedy metodu, která vykreslí obrazovku s aktuálními údaji o kole a životy bojovníků. Zprávy o útoku a obraně budeme chtít vypisovat s dramatickou pauzou, aby byl výsledný efekt lepší, uděláme si pro takový typ zprávy ještě pomocnou metodu. Začněme s vykreslením informační obrazovky:

private void vykresli()
{
        System.out.println("-------------- Aréna -------------- \n");
        System.out.println("Zdraví bojovníků: \n");
        System.out.printf("%s %s\n", bojovnik1, bojovnik1.grafickyZivot());
        System.out.printf("%s %s\n", bojovnik2, bojovnik2.grafickyZivot());
}

Metoda je privátní, budeme ji používat jen uvnitř třídy.

Další privátní metodou bude výpis zprávy s dramatickou pauzou:

private void vypisZpravu(String zprava)
{
        System.out.println(zprava);
        try {
            Thread.sleep(500);
        } catch (InterruptedException ex) {
            System.err.println("Chyba, nepovedlo se uspat vlákno");
        }
}

Kód je zřejmý až na třídu Thread, která umožňuje práci s vlákny. My z ní využijeme pouze metodu sleep(), která uspí vlákno programu na daný počet milisekund. S vlákny budeme pracovat až na konci seriálu. Bloky try-catch prozatím nebudeme řešit, nejsou zde důležité a budeme je probírat později, spokojíme se s tím, že jsou zde nutné.

Obě metody vlastně jen vypisují na konzoli, připadá mi zbytečné je zkoušet, přesuneme se tedy již k samotnému zápasu. Metoda zapas() nebude mít žádné parametry a nebude ani nic vracet. Uvnitř bude cyklus, který bude na střídačku volat útoky bojovníků navzájem a vypisovat informační obrazovku a zprávy. Metoda by mohla vypadat takto:

public void zapas()
{
        System.out.println("Vítejte v aréně!");
        System.out.printf("Dnes se utkají %s s %s! \n\n", bojovnik1, bojovnik2);
        System.out.println("Zápas může začít...");

        // cyklus s bojem
        while (bojovnik1.nazivu() && bojovnik2.nazivu())
        {
                bojovnik1.utoc(bojovnik2);
                vykresli();
                vypisZpravu(bojovnik1.vratPosledniZpravu()); // zpráva o útoku
                vypisZpravu(bojovnik2.vratPosledniZpravu()); // zpráva o obraně
                bojovnik2.utoc(bojovnik1);
                vykresli();
                vypisZpravu(bojovnik2.vratPosledniZpravu()); // zpráva o útoku
                vypisZpravu(bojovnik1.vratPosledniZpravu()); // zpráva o obraně
                System.out.println();
        }
}

Kód vypíše jednoduché informace a po stisku klávesy přejde do cyklu s bojem. Jedná se o while cyklus, který se opakuje, dokud jsou oba bojovníci naživu. První bojovník zaútočí na druhého, jeho útok vnitřně zavolá na druhém bojovníkovi obranu. Po útoku vykreslíme obrazovku s informacemi a dále zprávy o útoku a obraně pomocí naší metody vypisZpravu(), která po výpisu udělá dramatickou pauzu. To samé provedeme i pro druhého bojovníka.

Přesuňme se do TahovyBoj.java, vytvořme patřičné instance a zavolejme na aréně metodu zapas():

// vytvoření objektů
Kostka kostka = new Kostka(10);
Bojovnik zalgoren = new Bojovnik("Zalgoren", 100, 20, 10, kostka);
Bojovnik shadow = new Bojovnik("Shadow", 60, 18, 15, kostka);
Arena arena = new Arena(zalgoren, shadow, kostka);
// zápas
arena.zapas();

Charakteristiky hrdinů si můžete upravit dle libosti. Program spustíme:

Objektová aréna v Javě – simulace zápasu ve stolní hře

Výsledek je docela působivý. Objekty spolu komunikují, grafický život ubývá jak má, zážitek umocňuje dramatická pauza. Aréna má však 2 nedostatky.

  • V cyklu s bojem útočí první bojovník na druhého. Poté však vždy útočí i druhý bojovník, nehledě na to, zda ho první nezabil. Může tedy útočit již jako mrtvý. Podívejte se na screenshot výše, Shadow útočil jako poslední i když byl mrtvý. Až potom se vystoupilo z while cyklu. U prvního bojovníka tento problém není, u druhého musíme před útokem kontrolovat, zda je naživu.
  • Druhým nedostatkem je, že bojovníci vždy bojují ve stejném pořadí, čili zde "Zalgoren" má vždy výhodu. Pojďme vnést další prvek náhody a pomocí kostky rozhodněme, který z bojovníků bude začínat. Jelikož jsou bojovníci vždy dva, stačí hodit kostkou a podívat se, zda padlo číslo menší nebo rovné polovině počtu stěn kostky. Tedy např. pokud padne na desetistěnné kostce číslo do 5ti, začíná 2. bojovník, jinak začíná první. Zbývá zamyslet se nad tím, jak do kódu zanést prohazování bojovníků. Jistě by bylo velmi nepřehledné opodmínkovat příkazy ve while cyklu. Jelikož již víme, že v Javě fungují reference, není pro nás problém udělat si 2 proměnné, ve kterých budou instance bojovníků, nazvěme je jednoduše b1 a b2. Do těchto proměnných si na začátku dosadíme bojovníky bojovnik1 a bojovnik2 tak, jak potřebujeme. Můžeme tedy při pozitivním hodu kostkou dosadit do b1 bojovník2 a naopak, výsledkem bude, že začínat bude ten druhý. Kód cyklu se takto vůbec nezmění a zůstane stále přehledný a jednoduchý, jen místo bojovnik bude b.

Změněná verze včetně podmínky, aby nemohl útočit mrtvý bojovník, by mohla vypadat nějak takto:

public void zapas()
{
        // původní pořadí
        Bojovnik b1 = bojovnik1;
        Bojovnik b2 = bojovnik2;
        System.out.println("Vítejte v aréně!");
        System.out.printf("Dnes se utkají %s s %s! \n\n", bojovnik1, bojovnik2);
        // prohození bojovníků
        boolean zacinaBojovnik2 = (kostka.hod() <= kostka.vratPocetSten() / 2);
        if (zacinaBojovnik2)
        {
                b1 = bojovnik2;
                b2 = bojovnik1;
        }
        System.out.printf("Začínat bude bojovník %s! \n\nZápas může začít...", b1);

        // cyklus s bojem
        while (b1.nazivu() && b2.nazivu())
        {
                b1.utoc(b2);
                vykresli();
                vypisZpravu(b1.vratPosledniZpravu()); // zpráva o útoku
                vypisZpravu(b2.vratPosledniZpravu()); // zpráva o obraně
                if (b2.nazivu())
                {
                        b2.utoc(b1);
                        vykresli();
                        vypisZpravu(b2.vratPosledniZpravu()); // zpráva o útoku
                        vypisZpravu(b1.vratPosledniZpravu()); // zpráva o obraně
                }
                System.out.println();
        }
}

Program vyzkoušejme.

Objektová aréna v Javě – simulace zápasu ve stolní hře

Vidíme, že je vše již v pořádku. Gratuluji vám, pokud jste se dostali až sem a tutoriály opravdu četli a pochopili, máte základy objektového programování a dokážete tvořit rozumné aplikace :)

Příště se podíváme na objektově orientované programování podrobněji. V úvodu jsme si říkali, že OOP stojí na pilířích: zapouzdření, dědičnost a polymorfismus. První umíme již velmi dobře a modifikátor private je nám známý. Další dva nás čekají příště.


 

Stáhnout

Staženo 686x (27.78 kB)
Aplikace je včetně zdrojových kódů v jazyce java

 

  Aktivity (2)

Článek pro vás napsal David Čápka
Avatar
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.

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


 


Miniatura
Předchozí článek
Bojovník do arény
Miniatura
Následující článek
Dědičnost a polymorfismus

 

 

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

Avatar
Roman
Člen
Avatar
Roman:

Servus
Perfektný tutorial, stránku síce sledujem už dlhšie ale zaregistrovať sa sem ma napadlo až oveľa neskôr aj keď sa to sem nehodí len by som chcel povedať: som rád, že existujú ľudia čo si dali námahu s týmto všetkým "pod tým samozrejme myslím celú stránku nie len program :)" klobúk dole a dúfam že to vydrží aj tak naďalej prajem len veľa úspechov :)

 
Odpovědět  +2 25.6.2015 17:34
Avatar
Marek
Člen
Avatar
Marek:

Užasný tutoriál chvílema jsem sice an něj nadával tos e přiznávám :D seděl sjem u toho jak mamut a koukal se na to a přemýšlel co je špatně sakra. Pak jsem se dohnal k tomu že jsem to začal dokonce porovnávat řádek po řádku :D ,ale pak jsem na to konečně přišel chyběla tam někde nějaká čárka a odrazník. Bylo to jen mou únavou. Díky za vaši stránku je super :)

Odpovědět 25.7.2015 1:14
Nekecej a programuj
Avatar
Lubor Pešek
Člen
Avatar
Lubor Pešek:

Nazdar
chtěl jsem reagovat před měsícem, ale teď jsem měl své problémy, tak ti to píšu až včil:)

ten kód by vypadal asi takto:

private void zapas() {
        while (ncferrari.isNazivu() && ferrarie.isNazivu()) {
            vypis("\n\n*************************" + cisloKola + ". kolo" + "**************************");
            vypis("             NA ŘADĚ JE " + hracNaRade);
            hracNaRade.utoc(protivnik);
            vypis(hracNaRade.getMessage());
            vypis(protivnik.getMessage());
            ukazkaZdravi();
            prehodHrace();
            cisloKola++;
        }
    }

a pochopitelně to potřebuje definovat zmíněnou metodu prehodHrace:

private void prehodHrace() {
        if (hracNaRade == ncferrari) {
            hracNaRade = ferrarie;
            protivnik = ncferrari;
        } else {
            hracNaRade = ncferrari;
            protivnik = ferrarie;
        }
    }

PS1: je to jen ukázka dvou metod, které vyžadují další deklarace druhých metod a atributů. Samostatně vám fungovat nebudou - to pro ty, kteří kopírují cizí kódy na zkoušku k sobě do nových projektů (i já to dělávám:D)

PS2: ncferari a ferrari jsou bojovníci, ale já si je tak pojmenoval:) jsou to moje názvy postav z wowka:)

Odpovědět 12. ledna 13:49
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:

vypis() je to samé, jako v ukázkovém příkladu vypisZpravu()
ukazkaZdravi() je to samé, jako v ukázkovém příkladu vykresli()
getMessage je to samé, jako v ukázkovém příkladu vratPosledniZ­pravu(), ale já osobně už neumím používat slovíčka vrat, dej, navrat, nastav, udelej apod:) prostě settry a gettry a když to jde, tak v angličtině

atribut cisloKola je moje číslování kol, aby to bylo přehlednější
hracNaRade je další atribut, který v sobě uchovává referenci na hráče, který splní podmínku v metodě prehodHrace()

Odpovědět 12. ledna 13:56
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
Hans Deutscher:

Chtěl bych moc poděkovat za tento i všechny ostatní tutoriály! Jsem začátečník v programování, ale hrozně se mi to líbí a hrozně mě to baví. Kdyby se mi podařilo se tím jednou živit, byl bych moc rád.
Každopádně jsi mi zase ukázal nové způsoby, techniky a metody, za což ti moc děkuji. Vše jsem perfektně pochopil a vše funguje hladce a bez problémů.

 
Odpovědět 11. února 21:23
Avatar
Vít Pelc
Člen
Avatar
Vít Pelc:

Díky moc za tutoriál... jen bych chtěl nahlásit jeden menší problém. Když si dám vypsat grafický život, tak mi při plném zdraví vypíše buď jenom jeden "#" a nebo nevypíše žádný. Kód mám přesně podle vzoru (viz. tutoriál). Prosím poraďte, co s tím?

 
Odpovědět 12. března 22:27
Avatar
Honza Novotný:

Ahoj, v textu se píše "Kód vypíše jednoduché informace a po stisku klávesy přejde do cyklu s bojem." Po stisku klávesy? Takto napsaný kód pokračuje a na stisk klávesy nečeká. Něco jsem přehlídl? Respektive jak se dá udělat, aby se program pozastavil a počkal na stisk klávesy?

 
Odpovědět 27. července 12:36
Avatar
pocitac770
Redaktor
Avatar
Odpovídá na Honza Novotný
pocitac770:

Je to text okopírovaný z tutoriálu pro C#, tam to opravdu je..

Console.WriteLine("Vítejte v aréně!");
Console.WriteLine("Dnes se utkají {0} s {1}! \n", bojovnik1, bojovnik2);
Console.WriteLine("Zápas může začít...");
Console.ReadKey(); /* zde je ono čekání na klávesu */
// cyklus s bojem
while (bojovnik1.Nazivu() && bojovnik2.Nazivu())
.....

Bohužel Java toto pro základní konzoli nemá (tedy alespoň co vím), takže tato funkcionalita byla z programu odebrána, ale nějak se na to v textu zapomnělo.

Editováno 16. srpna 21:19
 
Odpovědět 16. srpna 21:17
Avatar
pocitac770
Redaktor
Avatar
pocitac770:

Jediné, co mě napadá jako náhrada je prázdné načtení řádku z konzole, ale to musíš použít enter (takže náhrada by mohla být "zmáčkněte enter pro pokračování"), s tím, že by jsi klasicky přidal do kódu řádky

System.out.println("Zmáčkněte ENTER pro pokračování");
new Scanner(System.in).nextLine();
 
Odpovědět 16. srpna 21:23
Avatar
Odpovídá na pocitac770
Honza Novotný:

Dík, asi to tak vyřeším :)

 
Odpovědět 30. srpna 17:06
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 20. Zobrazit vše