8. diel - Aréna s mágom (dedičnosť a polymorfizmus)
V minulej lekcii, Dedičnosť a polymorfizmus , sme si vysvetlili dedičnosť a polymorfizmus. Dnes máme sľúbené, že si ich vyskúšame v praxi. Bude to opäť na našej aréne, kde z bojovníka oddědíme mága. Tento C # .NET tutoriál už patrí k tým náročnejším a bude tomu tak aj u ďalších. Preto si priebežne precvičujte prácu s objektmi, skúšajte si naše cvičenia a tiež vymýšľajte nejaké svoje aplikácie, aby ste si zažili základné veci. To, že je tu prítomný celý online kurz neznamená, že ho celý naraz prečítate a pochopíte Snažte sa programovať priebežne.
Než začneme niečo písať, zhodneme sa na tom, čo by mal mág vedieť. Mág bude fungovať rovnako, ako bojovník. Okrem života bude mať však aj manu. Spočiatku bude mana plná. V prípade plnej many môže mág vykonať magický útok, ktorý bude mať pravdepodobne vyššie damage, ako útok normálne (ale samozrejme záleží na tom, ako si ho nastavíme). Tento útok manu vybije na0
. Každé kolo sa bude
mana zvyšovať o 10 a mág bude podnikať len bežný útok. Akonáhle sa mana
úplne doplní, opäť bude môcť magický útok použiť. Mana bude zobrazená
grafickým ukazovateľom, rovnako ako život.
Vytvoríme teda triedu Mag.cs
, zdedíme ju z
Bojovnik
a dodáme ju atribúty, ktoré chceme oproti bojovníkovi
navyše. Bude teda vyzerať takto (opäť si ju okomentujte):
class Mag: Bojovnik { private int mana; private int maxMana; private int magickyUtok; }
V mágovi nemáme zatiaľ prístup ku všetkým premenným, pretože sú v
bojovníkovi nastavené ako privátne. Musíme triedu Bojovnik
ľahko upraviť. Zmeníme modifikátory private
u atribútov na
protected
. Budeme potrebovať len
kostka
a jmeno
, ale pokojne nastavíme ako
protected
všetky atribúty charakteru, pretože sa v budúcnosti
môžu hodiť, keby sme sa rozhodli oddědit ďalšie typy bojovníkov. Naopak
atribút zprava
nie je vhodné nastavovať ako
protected
, pretože nesúvisí s bojovníkom, ale s nejakou
vnútornou logikou triedy. Trieda teda bude vyzerať nejako takto:
protected string jmeno; protected int zivot; protected int maxZivot; protected int utok; protected int obrana; protected Kostka kostka; private string zprava; ...
Prejdime ku konstruktoru.
Konštruktor potomka
C # nededia konstruktory! Je to pravdepodobne z toho dôvodu, že predpokladá, že potomok bude mať navyše nejaké atribúty a pôvodné konštruktor by u neho bol na škodu. To je aj náš prípad, pretože konštruktor mága bude brať oproti tomu z bojovníka navyše 2 parametre (mana a magický útok).
Definujeme si teda konštruktor v potomkovi, ktorý berie parametre potrebné pre vytvorenie bojovníka a niekoľko parametrov navyše pre mága.
V konštruktor potomkov je nutné vždy volať konštruktor predka. Je to z toho dôvodu, že bez volania konstruktoru nemusí byť inštancie správne inicializovaná. Konštruktor predka nevoláme iba v prípade, že žiadny nemá. Náš konštruktor musia mať samozrejme všetky parametre potrebné pre predka plus tie nové, čo má navyše potomok. Niektoré potom odovzdáme predkovi a niektoré si spracujeme sami. Konštruktor predka sa vykoná pred naším konstruktoru.
VC # .NET existuje kľúčové slovo base
,
ktoré je podobné nami už známemu this
. Na rozdiel od
this
, ktoré odkazuje na konkrétnu inštanciu triedy,
base
odkazuje na predka. My teda môžeme zavolať
konštruktor predka s danými parametrami a potom vykonať navyše
inicializáciu pre mága. VC # sa volanie konstruktoru predka píše do
hlavičky metódy.
Konštruktor mága bude teda vyzerať takto:
public Mag(string jmeno, int zivot, int utok, int obrana, Kostka kostka, int mana, int magickyUtok): base(jmeno, zivot, utok, obrana, kostka) { this.mana = mana; this.maxMana = mana; this.magickyUtok = magickyUtok; }
Rovnako môžeme volať aj iný konštruktor v tej istej triede (nie predka),
len namiesto base použijeme this
.
Presuňme sa teraz do Program.cs
a druhého bojovníka (Shadow)
zmeňme na mága, napr. Takto:
Bojovnik gandalf = new Mag("Gandalf", 60, 15, 12, kostka, 30, 45);
Zmenu samozrejme musíme urobiť aj v riadku, kde bojovníka do arény
vkladáme. Všimnite si, že mága ukladáme do premennej typu
Bojovnik
. Nič nám v tom nebráni, pretože bojovník je jeho
predok. Rovnako tak si môžeme typ premennej zmeniť na Mag
. Keď
aplikáciu teraz spustíme, bude fungovať úplne rovnako, ako predtým. Mág
všetko dedí z bojovníka a zatiaľ teda funguje ako bojovník.
Polymorfizmus a prepisovanie metód
Bolo by výhodné, keby objekt Arena
mohol s mágom pracovať
rovnako ako s bojovníkom. My už vieme, že takémuto mechanizmu hovoríme
polymorfizmus. Aréna zavolá na objekte metódu
Utoc()
so súperom v parametri. Nestará sa o to, či bude útok
vykonávať bojovník alebo mág, bude s nimi pracovať rovnako. U mága si teda
prepíšeme metódu Utoc()
z predka. Prepíšeme
zdedenú metódu tak, aby útok pracoval s mannou, hlavička metódy však
zostane rovnaká.
Aby sme mohli nejakú metódu prepísať, musí byť v predkovi označená
ako virtuálny. Nehľadajte za tým žiadnu vedu, jednoducho
pomocou kľúčového slova virtual
C # oznámime,
že si želáme, aby potomok mohol túto metódu prepísať. Hlavičku metódy v
Bojovnik.cs
teda zmeníme na:
public virtual void Utoc(Bojovnik souper)
Keď sme pri metódach, budeme ešte určite používať metódu
NastavZpravu()
, tá je však privátne. Označme ju ako
protected
:
protected void NastavZpravu(string zprava)
Pri návrhu bojovníka sme samozrejme mali myslieť na to, že sa z neho bude
dediť a už označiť vhodné atribúty a metódy ako protected
,
prípadne metódy ako virtuálne. Kľúčovým slovom virtual
je
označená metóda, ktorú možno v potomkovi prepísať, inak to nie je
možné. V tutoriále k bojovníkovi som vás tým však nechcel zbytočne
zaťažovať, preto musíme modifikátory zmeniť až teraz, kedy im rozumieme *
Metóda Utoc()
v bojovníkovi bude teda
public virtual
. Teraz sa vráťme do potomka a poďme ju
prepísať. Metódu normálne definujeme v Mag.cs
tak, ako sme
zvyknutí. Za modifikátorom public
však ešte použijeme
kľúčové slovo override
, ktoré značí, že
si sme vedomí toho, že sa metóda zdedila, ale prajeme si zmeniť jej
správanie.
public override void Utoc(Bojovnik souper)
Podobne sme prepisovali metódu ToString()
u našich objektov.
Každý objekt v C # je totiž odděděný od System.Object
,
ktorý obsahuje 4 metódy, jedna z nich je aj ToString()
. Pri jej
implementácii teda musíme použiť override
.
Správanie metódy Utoc()
nebude nijako zložité. Podľa
hodnoty many buď vykonáme bežný útok alebo útok magický. Hodnotu many
potom buď zvýšime o 10 alebo naopak znížime na 0
v prípade
magického útoku.
public override void Utoc(Bojovnik souper) { int uder = 0; // Mana nie je naplnená if (mana < maxMana) { mana += 10; if (mana > maxMana) mana = maxMana; uder = utok + kostka.hod(); NastavZpravu(String.Format("{0} útočí s úderem za {1} hp", jmeno, uder)); } else // Magický útok { uder = magickyUtok + kostka.hod(); NastavZpravu(String.Format("{0} použil mágiu za {1} hp", jmeno, uder)); mana = 0; } souper.BranSe(uder); }
Kód je asi zrozumiteľný. Všimnite si obmedzenia many na
maxMana
, môže sa nám totiž stať, že túto hodnotu presiahne,
keď ju zvyšujeme o 10. Keď sa nad kódom zamyslíme, tak útok vyššie v
podstate vykonáva pôvodnej metóda Utoc()
. Iste by bolo
prínosné zavolať podobu metódy na predkovi namiesto toho, aby sme správanie
odpisovali. K tomu opäť použijeme base
:
{CSHARP_OOP} class Mag: Bojovnik { private int mana; private int maxMana; private int magickyUtok; public Mag(string jmeno, int zivot, int utok, int obrana, Kostka kostka, int mana, int magickyUtok): base(jmeno, zivot, utok, obrana, kostka) { this.mana = mana; this.maxMana = mana; this.magickyUtok = magickyUtok; } public override void Utoc(Bojovnik souper) { // Mana nie je naplnená if (mana < maxMana) { mana += 10; if (mana > maxMana) mana = maxMana; base.Utoc(souper); } else // Magický útok { int uder = magickyUtok + kostka.hod(); NastavZpravu(String.Format("{0} použil mágiu za {1} hp", jmeno, uder)); souper.BranSe(uder); mana = 0; } } } {/CSHARP_OOP}
{CSHARP_OOP} {CSHARP_MAIN_BLOCK} // vytvorenie objektov Kostka kostka = new Kostka(10); Bojovnik zalgoren = new Bojovnik("Zalgoren", 100, 20, 10, kostka); Bojovnik gandalf = new Mag("Gandalf", 60, 15, 12, kostka, 30, 45); Arena arena = new Arena(zalgoren, gandalf, kostka); // zápas arena.Zapas(); Console.ReadKey(); {/CSHARP_MAIN_BLOCK} {/CSHARP_OOP}
{CSHARP_OOP} 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.Next(1, pocetSten + 1); } public override string ToString() { return String.Format("Kocka s {0} stenami", pocetSten); } } {/CSHARP_OOP}
{CSHARP_OOP} class Bojovnik { protected string jmeno; protected int zivot; protected int maxZivot; protected int utok; protected int obrana; protected 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 bool 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 += "#"; s = s.PadRight(celkem + 1); s += "]"; return s; } public virtual void Utoc(Bojovnik souper) { int uder = utok + kostka.hod(); NastavZpravu(String.Format("{0} útočia s úderom za {1} 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("{0} utrpel poškodenie {1} hp", jmeno, zraneni); if (zivot <= 0) { zivot = 0; zprava += " a zomrel"; } } else zprava = String.Format("{0} odrazil útok", jmeno); NastavZpravu(zprava); } protected void NastavZpravu(string zprava) { this.zprava = zprava; } public string VratPosledniZpravu() { return zprava; } public override string ToString() { return jmeno; } } {/CSHARP_OOP}
using System.Threading; {CSHARP_OOP} 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; } private void Vykresli() { Console.Clear(); Console.WriteLine("-------------- Aréna -------------- \n"); Console.WriteLine("Zdravie bojovníkov: \n"); Console.WriteLine("{0} {1}", bojovnik1, bojovnik1.GrafickyZivot()); Console.WriteLine("{0} {1}", bojovnik2, bojovnik2.GrafickyZivot()); } private void VypisZpravu(string zprava) { Console.WriteLine(zprava); Thread.Sleep(500); } public void Zapas() { // pôvodné poradie Bojovnik b1 = bojovnik1; Bojovnik b2 = bojovnik2; Console.WriteLine("Vitajte v aréne!"); Console.WriteLine("Dnes sa stretnú {0} s {1}! \n", bojovnik1, bojovnik2); // prehodenie bojovníkov bool zacinaBojovnik2 = (kostka.hod() <= kostka.VratPocetSten() / 2); if (zacinaBojovnik2) { b1 = bojovnik2; b2 = bojovnik1; } Console.WriteLine("Začínať bude bojovník {0}! \nZápas môže začať...", b1); Console.ReadKey(); // cyklus s bojom while (b1.Nazivu() && b2.Nazivu()) { b1.Utoc(b2); Vykresli(); VypisZpravu(b1.VratPosledniZpravu()); // správa o útoku VypisZpravu(b2.VratPosledniZpravu()); // správa o obrane if (b2.Nazivu()) { b2.Utoc(b1); Vykresli(); VypisZpravu(b2.VratPosledniZpravu()); // správa o útoku VypisZpravu(b1.VratPosledniZpravu()); // správa o obrane } Console.WriteLine(); } } } {/CSHARP_OOP}
Opäť vidíme, ako môžeme znovupoužívat kód. S dedičnosťou je spojené naozaj mnoho techník, ako si ušetriť prácu. V našom prípade to ušetrí niekoľko riadkov, ale u väčšieho projektu by to mohlo mať obrovský význam.
Aplikácia teraz funguje tak, ako má.
Konzolová aplikácia
-------------- Aréna --------------
Zdravie bojovníkov:
Zalgoren [############# ]
Gandalf [################# ]
Gandalf použil mágiu za 52 hp
Zalgoren utrpel poškodenie 36 hp
Aréna nás však neinformuje o mane mága, poďme to napraviť. Pridáme
mágovi verejnú metódu GrafickaMana()
, ktorá bude obdobne ako u
života vracať string
s grafickým ukazovateľom many.
Aby sme nemuseli logiku so zložením ukazovatele písať dvakrát, upravíme
metódu GrafickyZivot()
v Bojovnik.cs
. Pripomeňme
si, ako vyzerá:
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 += "#"; s = s.PadRight(celkem + 1); s += "]"; return s; }
Vidíme, že nie je výnimkou premenných zivot
a
maxZivot
na živote nijako závislá. Metódu premenujeme na
GrafickyUkazatel()
a dáme ju 2 parametre: aktuálnu hodnotu a
maximálnu hodnotu. Premenné zivot
a maxZivot
v tele
metódy potom nahradíme za aktualni
a maximalni
.
Modifikátor bude protected
, aby sme metódu mohli v potomkovi
použiť:
protected string GrafickyUkazatel(int aktualni, int maximalni) { string s = "["; int celkem = 20; double pocet = Math.Round(((double)aktualni / maximalni) * celkem); if ((pocet == 0) && (Nazivu())) pocet = 1; for (int i = 0; i < pocet; i++) s += "#"; s = s.PadRight(celkem + 1); s += "]"; return s; }
Metódu GrafickyZivot()
v Bojovnik.cs
naimplementujeme znovu, bude nám v nej stačiť jediný riadok a to zavolanie
metódy GrafickyUkazatel()
s príslušnými parametrami:
public string GrafickyZivot() { return GrafickyUkazatel(zivot, maxZivot); }
Určite som mohol v tutoriálu s bojovníkom urobiť metódu
GrafickyUkazatel()
rovno. Chcel som však, aby sme si ukázali, ako
sa rieši prípady, keď potrebujeme vykonať podobnú funkčnosť viackrát. S
takouto parametrizáciou sa v praxi budete stretávať často, pretože nikdy
presne nevieme, čo budeme v budúcnosti od nášho programu požadovať.
Teraz môžeme vykresľovať ukazovateľ tak, ako sa nám to hodí. Presuňme
sa do Mag.cs
a naimplementujme metódu GrafickaMana()
:
public string GrafickaMana() { return GrafickyUkazatel(mana, maxMana); }
Jednoduché, že? Teraz je mág hotový, zostáva len naučiť arénu
zobrazovať manu v prípade, že je bojovník mág. Presuňme sa teda do
Arena.cs
.
Rozpoznanie typu objektu
Keďže sa nám teraz vykreslenie bojovníka skomplikovalo, urobíme si na
neho samostatnú metódu VypisBojovnika()
, jej parametrom bude
daná inštancie bojovníka:
private void VypisBojovnika(Bojovnik b) { Console.WriteLine(b); Console.Write("Zivot: "); Console.WriteLine(b.GrafickyZivot()); }
Teraz poďme reagovať na to, či je bojovník mág. Minule sme si povedali,
že k tomu slúži operátor is
:
private void VypisBojovnika(Bojovnik b) { Console.WriteLine(b); Console.Write("Život: "); Console.WriteLine(b.GrafickyZivot()); if (b is Mag) { Console.Write("Mana: "); Console.WriteLine(((Mag)b).GrafickaMana()); } }
Bojovníka sme museli na mága pretypovať, aby sme sa k metóde
GrafickaMana()
dostali. Samotný Bojovnik
ju totiž
nemá. To by sme mali, VypisBojovnika()
budeme volať v metóde
Vykresli()
, ktorá bude vyzerať takto:
using System.Threading; {CSHARP_OOP} 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; } private void Vykresli() { Console.Clear(); Console.WriteLine("-------------- Aréna -------------- \n"); Console.WriteLine("Bojovníci: \n"); VypisBojovnika(bojovnik1); Console.WriteLine(); VypisBojovnika(bojovnik2); Console.WriteLine(); } private void VypisBojovnika(Bojovnik b) { Console.WriteLine(b); Console.Write("Život: "); Console.WriteLine(b.GrafickyZivot()); if (b is Mag) { Console.Write("Mana: "); Console.WriteLine(((Mag)b).GrafickaMana()); } } private void VypisZpravu(string zprava) { Console.WriteLine(zprava); Thread.Sleep(500); } public void Zapas() { // pôvodné poradie Bojovnik b1 = bojovnik1; Bojovnik b2 = bojovnik2; Console.WriteLine("Vitajte v aréne!"); Console.WriteLine("Dnes sa stretnú {0} s {1}! \n", bojovnik1, bojovnik2); // prehodenie bojovníkov bool zacinaBojovnik2 = (kostka.hod() <= kostka.VratPocetSten() / 2); if (zacinaBojovnik2) { b1 = bojovnik2; b2 = bojovnik1; } Console.WriteLine("Začínať bude bojovník {0}! \nZápas môže začať...", b1); Console.ReadKey(); // cyklus s bojom while (b1.Nazivu() && b2.Nazivu()) { b1.Utoc(b2); Vykresli(); VypisZpravu(b1.VratPosledniZpravu()); // správa o útoku VypisZpravu(b2.VratPosledniZpravu()); // správa o obrane if (b2.Nazivu()) { b2.Utoc(b1); Vykresli(); VypisZpravu(b2.VratPosledniZpravu()); // správa o útoku VypisZpravu(b1.VratPosledniZpravu()); // správa o obrane } Console.WriteLine(); } } } {/CSHARP_OOP}
{CSHARP_OOP} 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.Next(1, pocetSten + 1); } public override string ToString() { return String.Format("Kocka s {0} stenami", pocetSten); } } {/CSHARP_OOP}
{CSHARP_OOP} class Bojovnik { protected string jmeno; protected int zivot; protected int maxZivot; protected int utok; protected int obrana; protected 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 bool Nazivu() { return (zivot > 0); } protected string GrafickyUkazatel(int aktualni, int maximalni) { string s = "["; int celkem = 20; double pocet = Math.Round(((double)aktualni / maximalni) * celkem); if ((pocet == 0) && (Nazivu())) pocet = 1; for (int i = 0; i < pocet; i++) s += "#"; s = s.PadRight(celkem + 1); s += "]"; return s; } public string GrafickyZivot() { return GrafickyUkazatel(zivot, maxZivot); } public virtual void Utoc(Bojovnik souper) { int uder = utok + kostka.hod(); NastavZpravu(String.Format("{0} útočia s úderom za {1} 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("{0} utrpel poškodenie {1} hp", jmeno, zraneni); if (zivot <= 0) { zivot = 0; zprava += " a zemřel"; } } else zprava = String.Format("{0} odrazil útok", jmeno); NastavZpravu(zprava); } protected void NastavZpravu(string zprava) { this.zprava = zprava; } public string VratPosledniZpravu() { return zprava; } public override string ToString() { return jmeno; } } {/CSHARP_OOP}
{CSHARP_OOP} class Mag: Bojovnik { private int mana; private int maxMana; private int magickyUtok; public Mag(string jmeno, int zivot, int utok, int obrana, Kostka kostka, int mana, int magickyUtok): base(jmeno, zivot, utok, obrana, kostka) { this.mana = mana; this.maxMana = mana; this.magickyUtok = magickyUtok; } public string GrafickaMana() { return GrafickyUkazatel(mana, maxMana); } public override void Utoc(Bojovnik souper) { // Mana nie je naplnená if (mana < maxMana) { mana += 10; if (mana > maxMana) mana = maxMana; base.Utoc(souper); } else // Magický útok { int uder = magickyUtok + kostka.hod(); NastavZpravu(String.Format("{0} použil mágiu za {1} hp", jmeno, uder)); souper.BranSe(uder); mana = 0; } } } {/CSHARP_OOP}
{CSHARP_OOP} {CSHARP_MAIN_BLOCK} // vytvorenie objektov Kostka kostka = new Kostka(10); Bojovnik zalgoren = new Bojovnik("Zalgoren", 100, 20, 10, kostka); Bojovnik gandalf = new Mag("Gandalf", 60, 15, 12, kostka, 30, 45); Arena arena = new Arena(zalgoren, gandalf, kostka); // zápas arena.Zapas(); Console.ReadKey(); {/CSHARP_MAIN_BLOCK} {/CSHARP_OOP}
}
Hotovo
Konzolová aplikácia
-------------- Aréna --------------
Bojovníci:
Zalgoren
Život: [########## ]
Gandalf
Život: [##### ]
Mana: [############# ]
Zalgoren útočia s úderom za 28 hp
Aplikáciu ešte môžeme dodať krajší vzhľad, vložil som ASCIIart
nadpis Aréna, ktorý som vytvoril touto aplikáciou: http://patorjk.com/software/taag .
Navyše som zafarbil ukazovatele pomocou farby pozadia a popredia. Metódu k
vykreslenie ukazovatele som upravil tak, aby vykreslovala plný obdĺžnik
miesto #
(ten napíšete pomocou Alt + 2
1 9). Výsledok môže vyzerať takto:
Konzolová aplikácia
__ ____ ____ _ _ __
/__\ ( _ \( ___)( \( ) /__\
/(__)\ ) / )__) ) ( /(__)\
(__)(__)(_)\_)(____)(_)\_)(__)(__)
Bojovníci:
Zalgoren
Život: <span style="color:red">████</span><span style="color:maroon">████████████████</span>
Gandalf
Život: <span style="color:red">███████</span><span style="color:maroon">█████████████</span>
Mana: <span style="color:blue">█</span><span style="color:navy">███████████████████</span>
Gandalf použil mágiu za 48 hp
Zalgoren utrpel poškodenie 33 hp
Kód máte v prílohe. Ak ste niečomu nerozumeli, skúste si článok prečítať viackrát alebo pomalšie, sú to dôležité praktiky. V budúcej lekcii, Riešené úlohy k 5.-8. lekciu OOP v C # .NET , si vysvetlíme pojem statika.
V nasledujúcom cvičení, Riešené úlohy k 5.-8. lekciu OOP v C # .NET, si precvičíme nadobudnuté skúsenosti z predchádzajúcich lekcií.
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 3415x (40.04 kB)
Aplikácia je vrátane zdrojových kódov v jazyku C#