Pouze tento týden sleva až 80 % na e-learning týkající se C# .NET
Nauč se s námi víc. Využij 50% zdarma na e-learningové kurzy.
C# week

Lekce 5 - Bojovník do arény

V předešlém cvičení, Řešené úlohy k 4. lekci OOP v Javě, jsme si procvičili nabyté zkušenosti z předchozích lekcí.

Již tedy víme, jak fungují reference a jak můžeme s objekty zacházet. Bude se nám to hodit dnes i příště. Tento a příští tutoriál budou totiž věnovány dokončení naší arény. Hrací kostku již máme, ještě nám chybí další 2 objekty: bojovník a samotná aréna. Dnes se budeme věnovat bojovníkovi. Nejprve si popišme, co má bojovník umět, poté se pustíme do psaní kódu.

Atributy

Bojovník se bude nějak jmenovat a bude mít určitý počet hp (tedy života, např. 80hp). Budeme uchovávat jeho maximální život (bude se lišit u každé instance) a jeho současný život, tedy např. zraněný bojovník bude mít 40hp z 80ti. Bojovník má určitý útok a obranu, obojí vyjádřené opět v hp. Když bojovník útočí s útokem 20hp na druhého bojovníka s obranou 10hp, ubere mu 10hp života. Bojovník bude mít referenci na instanci objektu Kostka. Při útoku či obraně si vždy hodí kostkou a k útoku/obraně přičte padlé číslo. (Samozřejmě by mohl mít každý bojovník svou kostku, ale chtěl jsem se přiblížit stolní podobě hry a ukázat, jak OOP opravdu simuluje realitu. Bojovníci tedy budou sdílet jednu instanci kostky.) Kostkou dodáme hře prvek náhody, v realitě se jedná vlastně o štěstí, jak se útok nebo obrana vydaří. Konečně budeme chtít, aby bojovníci podávali zprávy o tom, co se děje, protože jinak by z toho uživatel nic neměl. Zpráva bude vypadat např. "Zalgoren útočí s úderem za 25hp.". Zprávami se zatím nebudeme zatěžovat a vrátíme se k nim až nakonec.

Již víme, co budeme dělat, pojďme na to! :) K projektu TahovyBoj si přidejme třídu Bojovnik a dodejme ji patřičné atributy. Všechny budou privátní:

class Bojovnik {
    /** Jméno bojovníka */
    private String jmeno;
    /** Život v HP */
    private int zivot;
    /** Maximální zdraví */
    private int maxZivot;
    /** Útok v HP */
    private int utok;
    /** Obrana v HP */
    private int obrana;
    /** Instance hrací kostky */
    private Kostka kostka;

}

Třída Kostka musí samozřejmě být v našem projektu.

Metody

Pojďme pro atributy vytvořit konstruktor, nebude to nic těžkého. Komentáře zde vynechám, vy si je dopište podobně, jako u atributů výše. Nebudu je psát ani u dalších metod, aby se tutoriál zbytečně neroztahoval a zůstal přehledný.

public Bojovnik(String jmeno, int zivot, int utok, int obrana, Kostka kostka) {
    this.jmeno = jmeno;
    this.zivot = zivot;
    this.maxZivot = zivot;
    this.utok = utok;
    this.obrana = obrana;
    this.kostka = kostka;
}

Všimněte si, že maximální zdraví si v konstruktoru odvodíme a nemáme na něj parametr v hlavičce metody. Předpokládáme, že bojovník je při vytvoření plně zdravý, stačí nám tedy znát pouze jeho život a maximální život bude stejný.

Přejděme k metodám, opět se nejprve zamysleme nad tím, co by měl bojovník umět. Začněme tím jednodušším, budeme chtít nějakou textovou reprezentaci, abychom mohli bojovníka vypsat. Překryjeme tedy metodu toString(), která vrátí jméno bojovníka. Určitě se nám bude hodit metoda, vracející zda je bojovník naživu (tedy typu boolean). Aby to bylo trochu zajímavější, budeme chtít kreslit život bojovníka do konzole, nebudeme tedy psát, kolik má života, ale "vykreslíme" ho takto:

[#########    ]

Výše uvedený život by odpovídal asi 70%. Dosud zmíněné metody nepotřebovaly žádné parametry. Samotný útok a obranu nechme na později a pojďme si implementovat toString(), nazivu() a grafickyZivot(). Začněme s toString(), tam není co vymýšlet:

@Override
public String toString() {
    return jmeno;
}

Nyní implementujme metodu nazivu(), opět to nebude nic těžkého. Stačí zkontrolovat, zda je život větší než 0 a podle toho se zachovat. Mohli bychom ji napsat třeba takto:

public boolean nazivu() {
    if (zivot > 0) {
        return true;
    } else {
        return false;
    }
}

Jelikož i samotný výraz (zivot > 0) je vlastně logická hodnota, můžeme vrátit tu a kód se značně zjednodušší:

public boolean nazivu() {
    return (zivot > 0);
}

Grafický život

Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

Jak jsem se již zmínil, metoda grafickyZivot() bude umožňovat vykreslit ukazatel života v grafické podobě. Již víme, že z hlediska objektového návrhu není vhodné, aby metoda objektu přímo vypisovala do konzole (pokud není k výpisu objekt určený), proto si znaky uložíme do řetězce a ten vrátíme pro pozdější vypsání. Ukážeme si kód metody a následně podrobně popíšeme:

public String grafickyZivot() {
    String s = "[";
    int celkem = 20;
    double pocet = Math.round(((double)zivot / maxZivot) * celkem);
    if ((pocet == 0) && (nazivu())) {
        pocet = 1;
    }
    for (int i = 0; i < pocet; i++) {
        s += "#";
    }
    for (int i = 0; i < celkem - pocet; i++) {
            s += " ";
    }
    s += "]";
    return s;
}

Připravíme si řetězec s a vložíme do něj úvodní znak "[". Určíme si celkovou délku ukazatele života do proměnné celkem (např. 20). Nyní v podstatě nepotřebujeme nic jiného, než trojčlenku. Pokud maxZivot odpovídá celkem dílků, zivot bude odpovídat pocet dílkům. pocet je proměnná s počtem dílků aktuálního zdraví.

Matematicky platí, že pocet = (zivot / maxZivot) * celkem;. My ještě doplníme zaokrouhlení na celé dílky a také přetypování jednoho z operandů na double, aby Java chápala dělení jako neceločíselné.

Měli bychom ošetřit případ, kdy je život tak nízký, že nám vyjde na 0 dílků, ale bojovník je stále naživu. V tom případě vykreslíme 1 dílek, jinak by to vypadalo, že je již mrtvý.

Dále stačí jednoduše for cyklem připojit k řetězci s patřičný počet znaků a doplnit je mezerami do celkové délky. Doplnění provedeme pomocí for cyklu, který přidává mezery do délky celkem. Přidáme koncový znak a řetězec vrátíme.

Vše si vyzkoušíme, přejděme k metodě main() a vytvořme si bojovníka (a kostku, protože tu musíme konstruktoru bojovníka předat). Následně vypišme, zda je naživu a jeho život graficky:

    Kostka kostka = new Kostka(10);
    Bojovnik bojovnik = new Bojovnik("Zalgoren", 100, 20, 10, kostka);

    System.out.printf("Bojovník: %s\n", bojovnik); // test toString();
    System.out.printf("Naživu: %s\n", bojovnik.nazivu()); // test nazivu();
    System.out.printf("Život: %s\n", bojovnik.grafickyZivot()); // test grafickyZivot();
import java.util.Random;
public class Kostka {

    private Random random;
    private int pocetSten;

    public Kostka() {
    pocetSten = 6;
    random = new Random();
    }

    public Kostka(int pocetSten) {
    this.pocetSten = pocetSten;
    random = new Random();
    }

    public int vratPocetSten() {
    return pocetSten;
    }

    public int hod() {
    return random.nextInt(pocetSten) + 1;
    }

    @Override
    public String toString() {
    return String.format("Kostka s %s stěnami", pocetSten);
    }
}
class Bojovnik {
    private String jmeno;
    private int zivot;
    private int maxZivot;
    private int utok;
    private int obrana;
    private Kostka kostka;

    public Bojovnik(String jmeno, int zivot, int utok, int obrana, Kostka kostka) {
    this.jmeno = jmeno;
    this.zivot = zivot;
    this.maxZivot = zivot;
    this.utok = utok;
    this.kostka = kostka;
    }

    public boolean nazivu() {
     return (zivot > 0);
    }

    public String grafickyZivot() {
    String s = "[";
    int celkem = 20;
    double pocet = Math.round(((double)zivot / maxZivot) * celkem);
    if ((pocet == 0) && (nazivu())) {
        pocet = 1;
    }
    for (int i = 0; i < pocet; i++) {
        s += "#";
    }
    for (int i = 0; i < celkem - pocet; i++) {
        s += " ";
    }
    s += "]";
    return s;
    }

    @Override
    public String toString() {
    return jmeno;
    }

}

Konzolová aplikace
Bojovník: Zalgoren
Naživu: true
Život: [####################]

Boj

Dostáváme se k samotnému boji. Implementujeme metody pro útok a obranu.

Obrana

Začněme obranou. Metoda branSe() bude umožňovat bránit se úderu, jehož síla bude předána metodě jako parametr. Metodu si opět ukážeme a poté popíšeme:

public void branSe(int uder) {
    int zraneni = uder - (obrana + kostka.hod());
    if (zraneni > 0) {
        zivot -= zraneni;
        if (zivot <= 0) {
            zivot = 0;
        }
    }
}

Nejprve spočítáme skutečné zranění a to tak, že z útoku nepřítele odečteme naši obranu zvýšenou o číslo, které padlo na hrací kostce. Pokud jsme zranění celé neodrazili (zraneni > 0), budeme snižovat náš život. Tato podmínka je důležitá, kdybychom zranění odrazili a bylo např. -2, bez podmínky by se život zvýšil. Po snížení života zkontrolujeme, zda není v záporné hodnotě a případně ho dorovnáme na nulu.

Útok

Metoda utoc() bude brát jako parametr instanci bojovníka, na kterého se útočí. To proto, abychom na něm mohli zavolat metodu branSe(), která na náš útok zareaguje a zmenší protivníkův život. Zde vidíme výhody referencí v Javě, můžeme si instance jednoduše předávat a volat na nich metody, aniž by došlo k jejich zkopírování. Jako první vypočteme úder, podobně jako při obraně, úder bude náš útok + hodnota z hrací kostky. Na soupeři následně zavoláme metodu branSe() s hodnotou úderu:

public void utoc(Bojovnik souper) {
    int uder = utok + kostka.hod();
    souper.branSe(uder);
}

To bychom měli, pojďme si zkusit v našem ukázkovém programu zaútočit a poté znovu vykreslit život. Pro jednoduchost nemusíme zakládat dalšího bojovníka, ale můžeme zaútočit sami na sebe:

    Kostka kostka = new Kostka(10);
    Bojovnik bojovnik = new Bojovnik("Zalgoren", 100, 20, 10, kostka);

    System.out.printf("Bojovník: %s\n", bojovnik); // test toString();
    System.out.printf("Naživu: %s\n", bojovnik.nazivu()); // test Nazivu();
    System.out.printf("Život: %s\n", bojovnik.grafickyZivot()); // test GrafickyZivot();

    bojovnik.utoc(bojovnik); // test útoku
    System.out.printf("Život po útoku: %s\n", bojovnik.grafickyZivot());
import java.util.Random;

public class Kostka {

    private Random random;
    private int pocetSten;

    public Kostka() {
    pocetSten = 6;
    random = new Random();
    }

    public Kostka(int pocetSten) {
    this.pocetSten = pocetSten;
    random = new Random();
    }

    public int vratPocetSten() {
    return pocetSten;
    }

    public int hod() {
    return random.nextInt(pocetSten) + 1;
    }

    @Override
    public String toString() {
     return String.format("Kostka s %s stěnami", pocetSten);
    }
}
class Bojovnik {
    private String jmeno;
    private int zivot;
    private int maxZivot;
    private int utok;
    private int obrana;
    private Kostka kostka;

    public Bojovnik(String jmeno, int zivot, int utok, int obrana, Kostka kostka) {
    this.jmeno = jmeno;
    this.zivot = zivot;
    this.maxZivot = zivot;
    this.utok = utok;
    this.obrana = obrana;
    this.kostka = kostka;
    }

    public boolean nazivu() {
    return (zivot > 0);
    }

    public String grafickyZivot() {
    String s = "[";
    int celkem = 20;
    double pocet = Math.round(((double)zivot / maxZivot) * celkem);
    if ((pocet == 0) && (nazivu())) {
        pocet = 1;
    }
    for (int i = 0; i < pocet; i++) {
        s += "#";
    }
    for (int i = 0; i < celkem - pocet; i++) {
        s += " ";
    }
    s += "]";
    return s;
    }

    public void branSe(int uder) {
    int zraneni = uder - (obrana + kostka.hod());
    if (zraneni > 0) {
        zivot -= zraneni;
        if (zivot <= 0) {
        zivot = 0;
        }
    }
    }

    public void utoc(Bojovnik souper) {
    int uder = utok + kostka.hod();
    souper.branSe(uder);
    }


    @Override
    public String toString() {
    return jmeno;
    }

}


Konzolová aplikace
Bojovník: Zalgoren
Naživu: true
Život: [####################]
Život po útoku: [##################  ]

Zdá se, že vše funguje, jak má. Přejděme k poslednímu bodu dnešního tutoriálu a to ke zprávám.

Zprávy

Jak již bylo řečeno, o útocích a obraně budeme uživatele informovat výpisem na konzoli. Výpis nebude provádět samotná třída Bojovnik, ta bude jen vracet zprávy jako textové řetězce. Jedna možnost by byla nastavit návratový typ metod utoc() a branSe() na String a při jejich volání vrátit i zprávu. Problém by však nastal v případě, když bychom chtěli získat zprávu od metody, která již něco vrací. Metoda samozřejmě nemůže jednoduše vrátit 2 věci.

Pojďme na věc univerzálněji, zprávu budeme ukládat do privátní proměnné zprava a uděláme metody pro její uložení a navrácení. Samozřejmě bychom mohli udělat proměnnou veřejnou, ale není zde důvod, proč umožnit zvenčí zápis do zprávy a také by skládání složitější zprávy uvnitř třídy mohlo být někdy problematické.

K atributům třídy tedy přidáme:

private String zprava;

Nyní si vytvoříme dvě metody. Privátní nastavZpravu(), která bere jako parametr text zprávy a slouží pro vnitřní účely třídy, kde nastaví zprávu do privátní proměnné:

private void nastavZpravu(String zprava) {
    this.zprava = zprava;
}

Nic složitého. Podobně jednoduchá bude veřejná metoda pro navrácení zprávy:

public String vratPosledniZpravu() {
    return zprava;
}

O práci se zprávami obohatíme naše metody utoc() a branSe(), nyní budou vypadat takto:

public void utoc(Bojovnik souper) {
    int uder = utok + kostka.hod();
    nastavZpravu(String.format("%s útočí s úderem za %s hp", jmeno, uder));
    souper.branSe(uder);
}

public void branSe(int uder) {
    int zraneni = uder - (obrana + kostka.hod());
    if (zraneni > 0) {
        zivot -= zraneni;
        zprava = String.format("%s utrpěl poškození %s hp", jmeno, zraneni);
        if (zivot <= 0) {
            zivot = 0;
            zprava += " a zemřel";
        }

        } else
        zprava = String.format("%s odrazil útok", jmeno);
    nastavZpravu(zprava);
}

Vše si opět vyzkoušíme, tentokrát již vytvoříme druhého bojovníka:

    Kostka kostka = new Kostka(10);
    Bojovnik bojovnik = new Bojovnik("Zalgoren", 100, 20, 10, kostka);

    System.out.printf("Život: %s\n", bojovnik.grafickyZivot()); // test GrafickyZivot();

    // útok na našeho bojovníka
    Bojovnik souper = new Bojovnik("Shadow", 60, 18, 15, kostka);
    souper.utoc(bojovnik);
    System.out.println(souper.vratPosledniZpravu());
    System.out.println(bojovnik.vratPosledniZpravu());

    System.out.printf("Život: %s\n", bojovnik.grafickyZivot());
import java.util.Random;

public class Kostka {

    private Random random;
    private int pocetSten;

    public Kostka() {
    pocetSten = 6;
    random = new Random();
    }

    public Kostka(int pocetSten) {
    this.pocetSten = pocetSten;
    random = new Random();
    }

    public int vratPocetSten() {
    return pocetSten;
    }

    public int hod() {
    return random.nextInt(pocetSten) + 1;
    }

    @Override
    public String toString() {
    return String.format("Kostka s %s stěnami", pocetSten);
    }
}
class Bojovnik {
    private String jmeno;
    private int zivot;
    private int maxZivot;
    private int utok;
    private int obrana;
    private Kostka kostka;
    private String zprava;

    public Bojovnik(String jmeno, int zivot, int utok, int obrana, Kostka kostka) {
    this.jmeno = jmeno;
    this.zivot = zivot;
    this.maxZivot = zivot;
    this.utok = utok;
    this.obrana = obrana;
    this.kostka = kostka;
    }

    public boolean nazivu() {
    return (zivot > 0);
    }

    public String grafickyZivot() {
    String s = "[";
    int celkem = 20;
    double pocet = Math.round(((double)zivot / maxZivot) * celkem);
    if ((pocet == 0) && (nazivu())) {
        pocet = 1;
    }
    for (int i = 0; i < pocet; i++) {
        s += "#";
    }
    for (int i = 0; i < celkem - pocet; i++) {
        s += " ";
    }
    s += "]";
    return s;
    }

    public void utoc(Bojovnik souper) {
    int uder = utok + kostka.hod();
    nastavZpravu(String.format("%s útočí s úderem za %s hp", jmeno, uder));
    souper.branSe(uder);
    }

    public void branSe(int uder) {
    int zraneni = uder - (obrana + kostka.hod());
    if (zraneni > 0) {
        zivot -= zraneni;
        zprava = String.format("%s utrpěl poškození %s hp", jmeno, zraneni);
        if (zivot <= 0) {
        zivot = 0;
        zprava += " a zemřel";
        }

    } else
        zprava = String.format("%s odrazil útok", jmeno);
        nastavZpravu(zprava);
    }

    private void nastavZpravu(String zprava) {
    this.zprava = zprava;
    }

    public String vratPosledniZpravu() {
    return zprava;
    }

    @Override
    public String toString() {
    return jmeno;
    }
}

Konzolová aplikace
Život: [####################]
Shadow útočí s úderem za 24 hp
Zalgoren utrpěl poškození 10 hp
Život: [##################  ]

Máme kostku i bojovníka, teď již chybí jen aréna.

Tu si vytvoříme hned v příští lekci, Java - Aréna s bojovníky.


 

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 1196x (4.45 kB)
Aplikace je včetně zdrojových kódů v jazyce Java

 

Předchozí článek
Řešené úlohy k 4. lekci OOP v Javě
Všechny články v sekci
Objektově orientované programování v Javě
Článek pro vás napsal David Čápka
Avatar
Jak se ti líbí článek?
27 hlasů
David je zakladatelem ITnetwork a programování se profesionálně věnuje 13 let. Má rád Nirvanu, sushi a svobodu podnikání.
Unicorn university David se informační technologie naučil na Unicorn University - prestižní soukromé vysoké škole IT a ekonomie.
Aktivity

 

 

Komentáře

Avatar

Neregistrovaný
Avatar
:8.10.2012 12:53

Přijde mi to poměrně zmatené. Chybí hlavičky metod (main, utoc - pro začátečníka dost matoucí, jsou sice na konci, ale tam se možná čitatel ani nedostane), navíc při výpisu stavů bojovníka chybí v kódu odřádkování \n, takže se to vypíše jinak než je ukázáno, opět zmate.
Špatně pojmenovaná metoda vratPosledniZ­pravu()(vytvá­ří špatné návyky při používání getrů a setrů), spíš bych volil jen vratZpravu().

 
Odpovědět
8.10.2012 12:53
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na
David Čápka:8.10.2012 13:15

Z tvého komentáře mám dojem, že si myslíš, že toto je samostatný tutoriál pro úplné začátečníky. Je to 16. tutoriál, ne první. Navíc navazuje na minulé díly, je to třída k již založenému projektu z minula.

Na výpis stavů se podívám.

Odpovědět
8.10.2012 13:15
Jsem moc rád, že jsi na síti, a přeji ti top IT kariéru, ať jako zaměstnanec nebo podnikatel. Máš na to! :)
Avatar
Unafený Konyk:4.1.2014 1:35

Zdravim, prosim o radu .. Vzdy ked sa pokusim vypisat grafickyZivot()v Main,program zivot na obrazovku nevypise a program sa pravdepodobne zacykli.(proc + ram 100%)

 
Odpovědět
4.1.2014 1:35
Avatar
Odpovídá na Unafený Konyk
Unafený Konyk:4.1.2014 13:35
public class Hrdina {
        public String meno;
        public int zivot;
        public int maxZivot;
        public int utok;
        public int obrana;
        public  Kocka kocka;



        public Hrdina (String getmeno,int getzivot,int getutok,int getobrana,Kocka getkocka){
                this.meno=getmeno;
                this.zivot=getzivot;
                this.utok=getutok;
                this.obrana=getobrana;
                this.kocka=getkocka;

        }

        public boolean nazivu()
        {
                if (zivot > 0)
                        return true;
                else
                        return false;
        }



        public void branSa(int uder){
                int zranenie = uder - (obrana+ kocka.hod());
                if (zranenie > 0)
                {zivot = zivot - zranenie;
                }
                if (zivot <=0)
                {zivot = 0;}
        }


        public void utoc(Hrdina souper)
        {
                int uder = utok + kocka.hod();

                souper.branSa(uder);
        }


        public String grafickyZivot()
    {
        String s = "[";
        int celkem = 20;
        double pocet = Math.round(((double)zivot / maxZivot) * celkem);
        if ((pocet == 0) && (nazivu()))
            pocet = 1;
        for (int i = 0; i < pocet; i++)
            s += "#";
        for (int i = 0; i < celkem - pocet; i++)
            s += " ";
        s += "]";
        return s;
    }



        @Override
        public String toString()
        {
                return meno;
        }
}
 
Odpovědět
4.1.2014 13:35
Avatar
adder
Člen
Avatar
adder:28.1.2014 16:13

nemá být správně v metodě branSe() vzužita metoda nastavZpravu?

nastavZpravu(String.format("%s odrazil utok", jmeno));

takhle? :)

Odpovědět
28.1.2014 16:13
I’m going to lay this brick as perfectly as a brick can be laid.
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na adder
David Čápka:21.3.2014 16:26

Jo, asi by to bylo lepší.

Odpovědět
21.3.2014 16:26
Jsem moc rád, že jsi na síti, a přeji ti top IT kariéru, ať jako zaměstnanec nebo podnikatel. Máš na to! :)
Avatar
DeamoniX
Člen
Avatar
Odpovídá na Unafený Konyk
DeamoniX:10.4.2014 22:43

vím, že nejspíš odpovídám pozdě, ale chce to nastavit maxZivot, hoď do konstruktoru

this.maxZivot = zivot;
 
Odpovědět
10.4.2014 22:43
Avatar
Unafený Konyk:12.4.2014 18:43

Áno už všetko ide ako má, takže ďakujem..

 
Odpovědět
12.4.2014 18:43
Avatar
Tomas Hlinovsky:8.12.2015 21:18

je to super jenom to vypisování mi připadá hrozně komplikovaný asi si to budu muset ještě pořádně projít.. jenom nechápu proč někdy tu zprávu někdy měníš přes metodu "nastavZpravu" a nekdy natvrdo zprava="blabla" coz mi teda prijde logictejsi.

 
Odpovědět
8.12.2015 21:18
Avatar
Lubor Pešek
Člen
Avatar
Lubor Pešek:10.12.2015 18:06

Když saháš na atributy cizí třídy
zprava="blabla"
tak porušuješ princip OOP - zapouzdření.

Proto se používají settry a gettry - metody, které zpřístupňují atributy třídy.

Jo a jen pár poznámek pro příklad XES240:

  • nepoužívej v deklaraci public. Potom ti ty atributy může měnit cizí třída, přesně jak jsem popisoval výše.
  • je pěkné, že zapojuješ gettry:) ale použít název parametru jako "getxxxx" je blbost:) getXxxx() - tak jsou pojmenované právě návratové metody.
  • naplňování atributů v konstruktoru pomocí slovíčka "this" se používá především v případě, kdy jsou oba názvy shodné (jak atribut třídy tak parametr konstruktoru/me­tody).

Ono to funguje i takto:

public class Test{

        private int cislo;

        public Test(int druheCislo){
                this.cislo = druheCislo;
        }
}

ale v tomto případě je lepší a čistější napsat toto:

public class Test{

        private int cislo;

        public Test(int druheCislo){
                cislo = druheCislo;
        }
}

¨

Pochopitelně jak jsem říkal - v případě shodnosti jmen, this tam být MUSÍ:
public class Test{

private int cislo;

public Test(int cislo){
this.cislo = cislo;
}
}

Odpovědět
10.12.2015 18: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.
Avatar
Erik Báča
Člen
Avatar
Erik Báča:7.3.2016 21:42

Nenapadá tu někoho, proč mi ho to vždycky zabije? nemůžu na to přijít :/

package Game;

public class Bojovnik {

        private String jmeno;
        private int zivot;
        private int maxzivot;
        private int utok;
        private int obrana;
        private Kostka kostka;

        public Bojovnik(String jmeno, int zivot, int utok, int obrana, Kostka kostka)
        {
                this.jmeno = jmeno;
                this.zivot = zivot;
                this.maxzivot = zivot;
                this.utok = utok;
                this.obrana = obrana;
                this.kostka = kostka;
        }

        public void branSe(int uder)
        {
                int zraneni = uder - (obrana + kostka.hod());
                if (zraneni > 0)
                {
                        zivot -= zraneni;
                        if (zivot <= 0);
                        {
                                zivot = 0;
                        }
                }
        }

        public void utoc(Bojovnik souper)
        {
                int uder = utok + kostka.hod();
                souper.branSe(uder);
        }

        public String grafickyZivot()
        {
                String s = "[";
                int celkem = 20;
                double pocet = Math.round(((double)zivot / maxzivot) * celkem);
                if ((pocet == 0) && (nazivu()))
                                pocet = 1;
                for (int i = 0; i < pocet; i++)
                        s += "#";
                for (int i = 0; i < celkem - pocet; i++)
                        s += " ";
                s += "]";
                return s;
        }

        public boolean nazivu()
        {
                return (zivot > 0);
        }

        @Override
        public String toString()
        {
                return jmeno;
        }

        public static void main(String[] args) {
                Kostka kostka = new Kostka(10);
                Bojovnik bojovnik = new Bojovnik("Zalgoren", 100, 20, 10, kostka);

                System.out.printf("Bojovník: %s\n", bojovnik); // test toString();
                System.out.printf("Naživu: %s\n", bojovnik.nazivu()); // test Nazivu();
                System.out.printf("Život: %s\n", bojovnik.grafickyZivot()); // test GrafickyZivot();

                bojovnik.utoc(bojovnik); // test útoku
                System.out.printf("Život po útoku: %s\n", bojovnik.grafickyZivot());
        }

}
Odpovědět
7.3.2016 21:42
Když mi dáš mínus, napiš proč!
Avatar
pocitac770
Redaktor
Avatar
Odpovídá na Erik Báča
pocitac770:8.3.2016 12:36
if (zivot <= 0);
    {
        zivot = 0;
    }

Za if se středník nepíše, tady způsobil, že se vynulování prpvede vždy

 
Odpovědět
8.3.2016 12:36
Avatar
David Novák
Redaktor
Avatar
Odpovídá na pocitac770
David Novák:8.3.2016 13:14

Jen bych doplnil - středník znamená konec příkazu. if (..) {..} je příkaz větvení. Když hned za podmínku dáš středník, tak je to vlastně prázdný příkaz a kód se pak dá přepsat třeba takto:

if (zivot <= 0)
{

}

{
    zivot = 0;
}

Tedy zivot = 0 se provede vždy, nezávisle na podmínce :)

Odpovědět
8.3.2016 13:14
Chyba je mezi klávesnicí a židlí.
Avatar
Erik Báča
Člen
Avatar
Odpovídá na David Novák
Erik Báča:8.3.2016 15:32

Díky, já to samozřejmě vím, byla to chyba z nepozornosti, ale ani za nic jsem ji nenašel... :D

Odpovědět
8.3.2016 15:32
Když mi dáš mínus, napiš proč!
Avatar
pocitac770
Redaktor
Avatar
Odpovídá na Erik Báča
pocitac770:8.3.2016 15:55

Když se něco takového příště stane, tak můžeš zkusit debug (nechápu, jak jsem bez toho mohl žít... :D )
Pokud nevíš, jak se používá, tak si to můžeš nahradit tím, že v nějakých kritických častech kódu si vypisuješ, kde se změní ona hodnota na tu divnou (například že máš najednou 0 životů... Kdepak jsme to přepisovali?).. Tím pak najdeš chybný řádek a vše jednoduše vyřešíš. Jak jsem již zmínil, debug to zlehčuje :)

Editováno 8.3.2016 15:57
 
Odpovědět
8.3.2016 15:55
Avatar
jan valenta
Člen
Avatar
jan valenta:10.9.2016 16:16

Zdravím, dlouho jsem si lámal hlavu s kouskem nastavováním zpráv a chtěl bych se zeptat, proč je zde toto:

nastavZpravu(Strin­g.format("%s útočí s úderem za %s hp", jmeno, uder));
...
private void nastavZpravu(String zprava)
{
this.zprava = zprava;
}

když vůbec nepotřebuji funkci nastavZpravu. V článku to autor vlastně využívá jen k tomu, že zprávu přiřazuje sama sobě, nijak to nemění přístup k ní (stále je private), ani výstup programu. Pokud se nastavZpravu odstrani a dá se místo ní jen zpráva = , vše funguje úplně stejně:

zprava = (String.format("%s útočí s úderem za %s hp", jmeno, uder));

nevíte někdo, co mi na tom uniklo?

 
Odpovědět
10.9.2016 16:16
Avatar
pocitac770
Redaktor
Avatar
Odpovídá na jan valenta
pocitac770:10.9.2016 16:22

Víceméně ti neuniklo nic. Ta metoda je tam jenom proto, aby vás to naučilo spíše přistupovat k atributům přes metody, ve kterých si vy případně dodatečnou funkcionalitu (metoda nebude zajišťovat jenom nastavení zprávy, ale třeba nějakou zpětnou kontrolu něčeho, teď mě nic smysluplného nenapadá :D ). Ten hlavní důvod je ale takové pasivní zažití se do používání getterů a setterů, jejihž správné použití a taktéž trochu květnatější vysvětlení mé odpovědi se dozvíš v pozdější (10.) lekci

Editováno 10.9.2016 16:22
 
Odpovědět
10.9.2016 16:22
Avatar
Roman Duchoň:25.9.2016 18:01

No já nevím, snad na to nedoplatím, ale jel jsem si víc podle sebe.
Grafické zobrazení život mi přišlo prozatím zbytečné, takže:

@Override
 public String toString()    {
     return jmeno +" HP:" + hp + "/" + hpmax;
 }

Nevím, zda konkrétní boj vychází z nějaké deskové hry, ale mnohem více reálné mi přišlo (náhodné číslo z (konstanty útoku) * (hod kostkou 1 -10)):

    public void utoc(Bojovnik obet)  {
        this.uder = utok * kostka.hod();
        this.branit = obet.obrana * obet.kostka.hod();
        zraneni = uder - branit;
// A dál samozřejmě podmínky pro práci se životem...

Takže výsledný boj probíhá tímto:

roman.utoc(souper);

Také doufám, že mi ty náhody dělají co mají. :) Ale simulace je taková nahodilá, tak snad jo.
Každopádně... návod zas moc fajn, snad na tu individualitu nedoplatím v dalším díle. :)

Odpovědět
25.9.2016 18:01
RD
Avatar
Craftmanek
Člen
Avatar
Craftmanek:19.1.2017 20:43
package tahovyboj;
public class Bojovnik {
    private String jmeno;
    private int maxZivot;
    private int zivot;
    private int utok;
    private int obrana;
    private Kostka kostka;
    public Bojovnik (String jmeno, int zivot, int utok, int obrana, Kostka kostka){
        this.jmeno = jmeno;
        this.maxZivot=maxZivot;
        this.zivot=zivot;
        this.utok=utok;
        this.obrana=obrana;
        this.kostka=kostka;
    }
    @Override
    public String toString(){
        return jmeno;
    }
    public boolean nazivu(){
        if (zivot>0){
            return true;
        }
        else{
            return false;
        }
    }
    public String grafickyZivot(){
        String s = "[";
        int celkem = 20;
        double pocet = Math.round(((double)zivot / maxZivot) * celkem);
        if ((pocet == 0) && (nazivu()))
                pocet = 1;
        for (int i = 0; i < pocet; i++)
                s += "#";
        for (int i = 0; i < celkem - pocet; i++)
            s += " ";
        s += "]";
        return s;
    }
}

Nenapadá někoho proč se mi vše vypíše, jen ne ten grafickyUtok() ? Todle mi potom vyjede...

run:
Bojovnik: Zalgoren
Naživu: true
Odpovědět
19.1.2017 20:43
Ten, kdo chce umět, ale neumí.
Avatar
Petr Vít
Člen
Avatar
Petr Vít:31.1.2017 6:22

Otazka:

double pocet = Math.round(((double)zivot/maxZivot)*celkem);

proc tam je ten druhy double v zavorkach ? ..Zjistil jsem ze bez neho mi to nepocita spravne.
Ale proc tam je a co dela, jsem se nedozvedel, a v clanku to vysvetleno take neni.
Diky

 
Odpovědět
31.1.2017 6:22
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
Petr Vít
Člen
Avatar
Odpovídá na Petr Vít
Petr Vít:31.1.2017 6:42

A take jsem se chtel jeste zeptat na toto:

public void utoc(Bojovnik souper)
        {
                int uder = utok + kostka.hod();
                souper.branSe(uder);

Metoda je public ale predavame ji argumenty. "Bojovnik souper".
To jsem jako vytvorili instanci souper v tride bojovnika ?
Diky

 
Odpovědět
31.1.2017 6:42
Avatar
vajkuba1234
Člen
Avatar
Odpovídá na Craftmanek
vajkuba1234:31.1.2017 11:40

Je treba ji volat v main metode. Ukaz jeji vypis pripadne...

Odpovědět
31.1.2017 11:40
No hope, no future, JUST WAR! For world peace Israel must be DESTROYED!
Avatar
vajkuba1234
Člen
Avatar
Odpovídá na Petr Vít
vajkuba1234:31.1.2017 11:45

K prvnimu prispevku: zde se jedna o tzv. pretypovani. Navic to tam uvedeno je. :)
Viz

Matematicky platí, že pocet = (zivot / maxZivot) * celkem;. My ještě doplníme zaokrouhlení na celé dílky a také přetypování jednoho z operandů na double, aby Java chápala dělení jako neceločíselné.

K druhe prispevku: nevytvoril jsi instanci, pouze jsi urcil, ze metoda bude prebirat argument typu Bojovnik, diky cemuz s nim muzes pracovat uvnitr one metody.

Odpovědět
31.1.2017 11:45
No hope, no future, JUST WAR! For world peace Israel must be DESTROYED!
Avatar
Petr Štechmüller
Překladatel
Avatar
Odpovídá na Petr Vít
Petr Štechmüller:31.1.2017 11:46

Ahoj,

double pocet = Math.round(((double)zivot/maxZivot)*celkem);

kdyby tam nebylo to přetypování, tak vlastně dělíš dva celočíselné typy a výsledek takového dělení je opět celočíselný. Pokud alespoň jedno číslo (dělenec, nebo dělitel) explicitně přetypuješ na double, tak výsledek bude právě také double.

Odpovědět
31.1.2017 11:46
Pokud spolu kód a komentář nekorespondují, budou patrně oba chybné
Avatar
Odpovídá na Craftmanek
Peter Gasparik:15.3.2017 11:41

v konstruktore to mas zle definovane, ma to byt takto: this.maxZivot = zivot ;

Editováno 15.3.2017 11:43
Odpovědět
15.3.2017 11:41
while (noSuccess) { tryAgain(); if (Dead) break;
Avatar
Adam Bucher
Člen
Avatar
Adam Bucher:22.5.2017 12:31

V předposledním kódu je dvakrát chyba - pro vypsání hodnoty v int proměnné je použito %s:

public void utoc(Bojovnik souper) {
        int uder = utok + kostka.hod();
        nastavZpravu(String.format("%s útočí s úderem za %s hp", jmeno, uder)); // int vypsaný pomocí %s
        souper.branSe(uder);
}

public void branSe(int uder) {
        int zraneni = uder - (obrana + kostka.hod());
        if (zraneni > 0) {
                zivot -= zraneni;
                zprava = String.format("%s utrpěl poškození %s hp", jmeno, zraneni); // int vypsaný pomocí %s
                if (zivot <= 0) {
                        zivot = 0;
                        zprava += " a zemřel";
                }

        } else
                zprava = String.format("%s odrazil útok", jmeno);
        nastavZpravu(zprava);
}

Chytrá Java to sice spoklne, ale podle mě to není vhodné.
Pokud to má nějaký účel, prosím o vysvětlení.

 
Odpovědět
22.5.2017 12:31
Avatar
Dušan Kolesár:12.10.2017 18:38

Ahoj
Chcel by som sa opytat. Domnievam sa spravne, ze utoci sam na seba?
dakujem

 
Odpovědět
12.10.2017 18:38
Avatar
pocitac770
Redaktor
Avatar
Odpovídá na Dušan Kolesár
pocitac770:14.10.2017 23:08

Ve druhém spustitelném kódu ano, ve třetím již ne, tam jsme si vytvořilo 2 instance, kde vždy dáváme tu druhou jako parametr pro útočení

 
Odpovědět
14.10.2017 23:08
Avatar
Dušan Kolesár:19.10.2017 15:22

Ahoj, este jednu otazku by som mal.
V metode: public void utoc(Bojovnik souper) volam metodu souper.branSe(uder)
Ako parameter metody utoc beriem instanciu bojovnik a nie souper. Pravdepodobne to je jedno, ale nemalo by to byt nasledovne?

public void utoc(Bojovnik bojovnik) {
int uder = utok + kostka.hod();
nastavZpravu(Strin­g.format("%s útočí s úderem za %s hp", jmeno, uder));
bojovnik.bran­Se(uder);

Dakujem pekne

 
Odpovědět
19.10.2017 15:22
Avatar
Marek Zelený
Redaktor
Avatar
Odpovídá na Dušan Kolesár
Marek Zelený:22.10.2017 19:01

Ahoj, když vytváříš metodu s nějakými parametry, napíšeš do závorky vždy datový typ parametru (v našem případě Bojovnik) a jeho jméno. Na jménu vlastně vůbec nezáleží, může být jakékoliv, třeba jenom "x". V podstatě se vytvoří jen nová reference, která ukazuje na ten samý objekt, který metodě předáme v parametru.

public void utoc(Bojovnik x) {
        int uder = utok + kostka.hod();
        x.branSe(uder);
}

Když potom v hlavní části programu (main) vytvoříme instanci třídy Bojovnik, která se jmenuje třeba "klaun" a zaútočíme na ni, budou v paměti uložené dvě reference na ten samý objekt. Jedna bude proměnná "klaun", druhá se bude jmenovat "x" (jak jsme ji nazvali v metodě nahoře), obě ale budou ukazovat na bojovníka jménem Shadow se 60 životy.

Bojovnik bojovnik = new Bojovnik("Zalgoren", 100, 20, 10, kostka);
Bojovnik klaun= new Bojovnik("Shadow", 60, 18, 15, kostka);
bojovnik.utoc(klaun);

Proměnné si zkrátka můžeš pojmenovat úplně jak chceš, jen si musíš dávat pozor, aby ses v kódu vyznal a věděl, co je pod kterým jménem uložené :-).

 
Odpovědět
22.10.2017 19:01
Avatar
Marek Zelený
Redaktor
Avatar
Marek Zelený:22.10.2017 19:54

David Čápka Mám pocit, že kód, který upravuje zprávu v metodě branSe, není úplně vychytaný. Nejdříve totiž přímo zasahuje do privátní proměnné "zprava" a upravuje ji, ale na konci ještě používá metodu nastavZpravu, které navíc jako argument předává tu samou proměnnou, kterou metoda upravuje.

private String zprava;

private void nastavZpravu(String zprava) {
        this.zprava = zprava;
}

public void branSe(int uder) {
        int zraneni = uder - (obrana + kostka.hod());
        if (zraneni > 0) {
                zivot -= zraneni;
                zprava = String.format("%s utrpěl poškození %s hp", jmeno, zraneni);
                if (zivot <= 0) {
                        zivot = 0;
                        zprava += " a zemřel";
                }

        } else
                zprava = String.format("%s odrazil útok", jmeno);
        nastavZpravu(zprava);
}

V metodě branSe tedy nejspíš chybí deklarace vlastní proměnné "zprava", kterou by pak předala metodě nastavZpravu (když už je potřeba to dělat přes ni) a tím ji uložila do proměnné samotné instance bojovníka. Takhle ten kód z mého pohledu nedává smysl.

 
Odpovědět
22.10.2017 19:54
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Marek Zelený
David Čápka:22.10.2017 20:18

Pravda, na začátku by měla být deklarována lokální proměnná String zprava;. Při další revizi kurzu to opravím, na funkčnost to nemá vliv.

Odpovědět
22.10.2017 20:18
Jsem moc rád, že jsi na síti, a přeji ti top IT kariéru, ať jako zaměstnanec nebo podnikatel. Máš na to! :)
Avatar
 
Odpovědět
24.10.2017 15:27
Avatar
Martin Konečný:19.3.2019 11:06

Ahoj,

chtěl bych požádat o vysvětlení ohledně výpisu zpráv. Nechápu jakým způsobem se používá ta proměnná "zprava".
Při útoku bojovníka se v metodě utoc do proměnné "zprava" uloží text zprávy o útoku. Vzápětí se v metodě branSe do té samé proměnné uloží jiná zpráva. Takže jsem se domníval, že tím se ta původní zpráva přepíše.
Samozřejmě to asi bude tak, že oba bojovníci ukládají ten text do své vlastní proměnné "zprava".
No ale tomu právě nerozumím. Nikde jsme tomu danému bojovníkovi nepřidělili vlastní proměnou "zprava". Zdánlivě tedy oba používají jednu a tu samou proměnnou "zprava", která patří dané třídě a mělo by tedy být jedno, jestli zavolám souper.vratPos­ledniZpravu(), nebo bojovnik.vrat­PosledniZpravu(). V obou případech bych čekal výpis aktuální hodnoty proměnné zpráva.

 
Odpovědět
19.3.2019 11:06
Avatar
Petr Štechmüller
Překladatel
Avatar
Odpovídá na Martin Konečný
Petr Štechmüller:19.3.2019 11:23

Ahoj, zpráva je instanční proměnná, to znamená, že každý nově vytvořený bojovník má vlastní proměnnou zpráva. Proto si každý může nastavovat vlastní zprávu a metoda vratPosledniZ­pravu opravdu udělá to, co se od ní očekává.

Odpovědět
19.3.2019 11:23
Pokud spolu kód a komentář nekorespondují, budou patrně oba chybné
Avatar
Odpovídá na Petr Štechmüller
Martin Konečný:19.3.2019 12:22

Super, díky moc za vysvětlení.

 
Odpovědět
19.3.2019 12:22
Avatar
Matěj Bína
Člen
Avatar
Matěj Bína:6.4.2019 19:29

Chtěl jsem trochu vylepšit hlášku o smrti bojovníka a napsal

if (zivoty < 0) {
        zivoty = 0;
        zprava += String.format("%s je mrtev.", jmeno);
}

To ale vyvolalo kaskádu chyb. Zjistil jsem, že k tomu je potřeba použít metodu concat(), kterou jsem ve svých poznámkách z tutoriálů nenašel. Pokud to chcete použít, správně (snad) to je:

if (zivoty < 0) {
        zivoty = 0;
        zprava = zprava.concat(String.format("%s je mrtev.", jmeno));
}
 
Odpovědět
6.4.2019 19:29
Avatar
Patrik Vala
Člen
Avatar
Patrik Vala:28.3.2020 21:16

Ahoj, jak je možné, že na public void utoc(Bojovnik souper) můžu zavolat Bojovnik bojovnik?

 
Odpovědět
28.3.2020 21:16
Avatar
Lubor Pešek
Člen
Avatar
Lubor Pešek:17. března 13:05

Možná s tím nikdo neměl problémy, ale můžete se nad tím zamyslet - v poslední části tohoto článku - Zprávy
se v úvodu píše, že výpis nebude mít na starosti samotný bojovník. No a potom následuje postup, že přidáme nový atribut a metody.
Ale dovedu si představit, že by se někdo mohl zeptat kam je přidáme (do které třídy).

Ono to potom vyjde docela najevo, ale může to někoho zmást. Tak to berte jako podnět k zamyšlení, jestli to tam nezkonkretizovat.

Odpovědět
17. března 13: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.
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 39 zpráv z 39.