Lekce 8 - Aréna s mágem (dědičnost a polymorfismus) ve Swift
V minulé lekci, Dědičnost a polymorfismus ve Swift, jsme si vysvětlili dědičnost a polymorfismus.
Dnes máme slíbeno, že si je vyzkoušíme v praxi. Bude to opět na naší aréně, kde z bojovníka oddědíme mága. Tento Swift tutoriál již patří k těm náročnějším a bude tomu tak i u dalších. Proto si průběžně procvičujte práci s objekty a také vymýšlejte nějaké své aplikace, abyste si zažili základní věci. To, že je tu přítomen celý online kurz neznamená, že ho celý najednou přečtete a pochopíte Snažte se programovat průběžně.
Než začneme něco psát, shodněme se na tom, co by měl mág umět. Mág
bude fungovat stejně, jako bojovník. Kromě života bude mít však i
manu. Zpočátku bude mana plná. V případě plné many
může mág vykonat magický útok, který bude mít
pravděpodobně vyšší damage, než útok normální (ale samozřejmě
záleží na tom, jak si ho nastavíme). Tento útok manu vybije na
0
. Každé kolo se bude mana zvyšovat o 10 a mág bude podnikat
jen běžný útok. Jakmile se mana zcela doplní, opět bude moci magický
útok použít. Mana bude zobrazena grafickým ukazatelem, stejně jako
život.
Vytvoříme tedy třídu Mag
v souboru
Bojovnik.swift
, zdědíme ji z Bojovnik
a dodáme ji
vlastnosti, které chceme oproti bojovníkovi navíc. Bude tedy vypadat
takto:
class Mag: Bojovnik { private var mana : Double private var maxMana : Double private var magickyUtok : Int }
V mágovi nemáme zatím přístup ke všem proměnným, protože jsou v
bojovníkovi nastavené jako privátní. Musíme třídu Bojovnik
lehce upravit. Změníme modifikátory private
u vlastností na
fileprivate
. Budeme potřebovat jen
kostka
a jmeno
, ale klidně nastavíme jako
fileprivate
všechny vlastnosti charakteru, protože se v budoucnu
mohou hodit, kdybychom se rozhodli oddědit další typy bojovníků. Naopak
vlastnost zprava
není vhodné nastavovat jako
fileprivate
, protože nesouvisí s bojovníkem, ale s nějakou
vnitřní logikou třídy. Třída tedy bude vypadat nějak takto:
class Bojovnik { fileprivate var jmeno : String fileprivate var zivot : Double fileprivate var maxZivot : Double fileprivate var utok : Int fileprivate var obrana : Int fileprivate var kostka : Kostka private var zprava : String = "" // ...
Přejděme ke konstruktoru.
Více konstruktorů ve Swift
Nastala ideální příležitost vysvětlit si, jak ve Swiftu funguje více
konstruktorů, respektive metod init()
. Swift rozlišuje tzv.
designated a convenience konstruktory. Designated by se dalo nejlépe přeložit
jako "označený" a je to vlastně primární/výchozí konstruktor. Pokud máme
pouze jeden init()
, tak je automaticky designated.
Jestliže chceme mít ve třídě více konstruktorů, aby se dala její
instance vytvořit na základě různých parametrů, tak musíme mít ty
další označené slovíčkem convenience
, což by se dalo
označit jako pohodlný. A tyto konstruktory musí přes self
volat
designated konstruktor. Není to nic složitého a můžeme si ukázat
jednoduchý případ, pokud bychom chtěli našeho bojovníka vytvořit bez
parametrů. Vypadalo by to asi takto:
init(jmeno: String, zivot: Int, utok: Int, obrana: Int, kostka: Kostka) { self.jmeno = jmeno self.zivot = Double(zivot) self.maxZivot = self.zivot self.utok = utok self.obrana = obrana self.kostka = kostka } convenience init() { self.init(jmeno: "Standardní válečník", zivot: 100, utok: 20, obrana: 10, kostka: Kostka()) }
Nyní můžeme zadat parametry bojovníka, použít první konstruktor, ale i
napsat pouze new Bojovnik()
, čímž se použije konstruktor
druhý. Ten pomocí volání designated konstruktoru nastaví výchozí
hodnoty.
Konstruktor potomka
Swift dědí konstruktory pouze ve specifických případech. Konstruktor
bude zděděn, pokud novým vlastnostem potomka nastavíme výchozí hodnoty
(nebo budou Optional
) a tím pádem není vyžadován konstruktor.
Stejně tak nesmíme vytvořit designated init()
metodu, abychom
nepřišli o zděděný konstruktor. Convenience konstruktory je možné
přidat.
V našem případě Mága bude lepší vytvořit vlastní konstruktor, protože máme vlastnosti navíc, které v něm chceme nastavovat.
Definujeme si tedy konstruktor v potomkovi, který bere parametry potřebné pro vytvoření bojovníka a několik parametrů navíc pro mága.
V konstruktorech potomků je nutné vždy volat konstruktor předka. Je to z toho důvodu, že bez volání konstruktoru nemusí být instance správně inicializovaná. Konstruktor předka nevoláme pouze v případě, že žádný nemá. Náš konstruktor musí mít samozřejmě všechny parametry potřebné pro předka plus ty nové, co má navíc potomek. Některé potom předáme předkovi a některé si zpracujeme sami. Konstruktor předka je nutné zavolat až nakonec, jinak Swift zobrazí chybu.
Ve Swift existuje klíčové slovo super
,
které je podobné námi již známému self
. Na rozdíl od
self
, které odkazuje na konkrétní instanci třídy,
super
odkazuje na předka. My tedy můžeme
zavolat konstruktor předka s danými parametry a poté vykonat navíc
inicializaci pro mága.
Konstruktor mága bude tedy vypadat takto:
init(jmeno: String, zivot: Int, utok: Int, obrana: Int, kostka: Kostka, mana: Int, magickyUtok: Int) { self.mana = Double(mana) self.maxMana = self.mana self.magickyUtok = magickyUtok super.init(jmeno: jmeno, zivot: zivot, utok: utok, obrana: obrana, kostka: kostka) }
Stejně můžeme volat i jiný konstruktor v té samé třídě
(ne předka), jen místo super
použijeme self
.
Opět jsme si převedli manu interně na Double
, brzy uvidíte
proč.
Přesuňme se nyní do main.swift
a druhého bojovníka (Shadow)
změňme na mága, např. takto:
let gandalf : Bojovnik = Mag(jmeno: "Gandalf", zivot: 60, utok: 15, obrana: 12, kostka: kostka, mana: 30, magickyUtok: 45)
Změnu samozřejmě musíme udělat i v řádku, kde bojovníka do arény
vkládáme. Všimněte si, že mága ukládáme do proměnné typu
Bojovnik
. Nic nám v tom nebrání, protože bojovník je jeho
předek. Stejně tak si můžeme typ proměnné změnit na Mag
.
Když aplikaci nyní spustíme, bude fungovat úplně stejně, jako předtím.
Mág vše dědí z bojovníka a zatím tedy funguje jako bojovník.
Polymorfismus a přepisování metod
Bylo by výhodné, kdyby objekt Arena
mohl s mágem pracovat
stejným způsobem jako s bojovníkem. My již víme, že takovémuto mechanismu
říkáme polymorfismus. Aréna zavolá na objektu metodu
utoc()
se soupeřem v parametru. Nestará se o to, jestli bude
útok vykonávat bojovník nebo mág, bude s nimi pracovat stejně. U mága si
tedy přepíšeme metodu utoc()
z předka.
Přepíšeme zděděnou metodu tak, aby útok pracoval s manou, hlavička metody
však zůstane stejná.
Přepsání metody z předka provedeme v potomkovi pomocí slovíčka
override
, jak si ukážeme níže.
Když jsme u metod, budeme ještě jistě používat metodu
nastavZpravu()
, ta je však privátní. Označme ji jako
fileprivate
:
fileprivate func nastavZpravu(_ zprava: String)
Při návrhu bojovníka jsme samozřejmě měli myslet na to, že se z něj
bude dědit a již označit vhodné vlastnosti a metody jako
fileprivate
.
Nyní se vraťme do potomka a pojďme přepsat metodu utoc()
.
Metodu normálně definujeme v Mag.swift
tak, jak jsme zvyklí.
Její definici ale začneme slovem override
,
které značí, že si jsme vědomi toho, že se metoda zdědila, ale přejeme
si změnit její chování.
override func utoc(souper: Bojovnik)
Chování metody utoc()
nebude nijak složité. Podle hodnoty
many buď provedeme běžný útok nebo útok magický. Hodnotu many potom buď
zvýšíme o 10 nebo naopak snížíme na 0
v případě magického
útoku.
override func utoc(souper: Bojovnik) { var uder = 0 // Mana není naplněna if mana < maxMana { mana += 10; if (mana > maxMana) { mana = maxMana } uder = utok + kostka.hod(); nastavZpravu("\(jmeno) útočí s úderem za \(uder) hp") } else { // Magický útok uder = magickyUtok + kostka.hod() nastavZpravu("\(jmeno) použil magii za \(uder) hp") mana = 0 } souper.branSe(uder: uder) }
Kód je asi srozumitelný. Všimněte si omezení many na
maxMana
, může se nám totiž stát, že tuto hodnotu
přesáhneme, když ji zvyšujeme o 10. Když se nad kódem zamyslíme, tak
útok výše v podstatě vykonává původní metoda utoc()
. Jistě
by bylo přínosné zavolat podobu metody na předkovi místo toho, abychom
chování opisovali. K tomu opět použijeme super
:
{SWIFT} class Bojovnik: CustomStringConvertible { fileprivate var jmeno : String fileprivate var zivot : Double fileprivate var maxZivot : Double fileprivate var utok : Int fileprivate var obrana : Int fileprivate var kostka : Kostka private var zprava : String = "" init(jmeno: String, zivot: Int, utok: Int, obrana: Int, kostka: Kostka) { self.jmeno = jmeno self.zivot = Double(zivot) self.maxZivot = self.zivot self.utok = utok self.obrana = obrana self.kostka = kostka } convenience init() { self.init(jmeno: "Standardní válečník", zivot: 100, utok: 20, obrana: 10, kostka: Kostka()) } var description: String { return jmeno } func nazivu() -> Bool { return zivot > 0 } func grafickyZivot() -> String { var s = "[" let celkem : Double = 20 var pocet : Double = round((zivot / maxZivot) * celkem) if (pocet == 0) && (nazivu()) { pocet = 1 } for _ in 0..<Int(pocet) { s += "#" } s = s.padding(toLength: Int(celkem) + 1, withPad: " ", startingAt: 0) s += "]" return s } func utoc(souper: Bojovnik) { let uder = utok + kostka.hod() nastavZpravu("\(jmeno) útočí s úderem za \(uder) hp") souper.branSe(uder: uder) } func branSe(uder: Int) { let zraneni = Double(uder - (obrana + kostka.hod())) var zprava = "" if (zraneni > 0) { zivot -= zraneni zprava = "\(jmeno) utrpěl poškození \(Int(zraneni)) hp" if (zivot <= 0) { zivot = 0 } } else { zprava = "\(jmeno) odrazil útok" } nastavZpravu(zprava) } fileprivate func nastavZpravu(_ zprava: String) { self.zprava = zprava } func vratPosledniZpravu() -> String { return zprava } } class Mag: Bojovnik { private var mana : Double private var maxMana : Double private var magickyUtok : Int init(jmeno: String, zivot: Int, utok: Int, obrana: Int, kostka: Kostka, mana: Int, magickyUtok: Int) { self.mana = Double(mana) self.maxMana = self.mana self.magickyUtok = magickyUtok super.init(jmeno: jmeno, zivot: zivot, utok: utok, obrana: obrana, kostka: kostka) } override func utoc(souper: Bojovnik) { var uder = 0 // Mana není naplněna if mana < maxMana { mana += 10; if (mana > maxMana) { mana = maxMana } super.utoc(souper: souper) nastavZpravu("\(jmeno) útočí s úderem za \(uder) hp") } else { // Magický útok uder = magickyUtok + kostka.hod() nastavZpravu("\(jmeno) použil magii za \(uder) hp") mana = 0 } souper.branSe(uder: uder) } } {/SWIFT}
{SWIFT} class Arena { private var bojovnik1 : Bojovnik private var bojovnik2 : Bojovnik private var kostka : Kostka init(bojovnik1: Bojovnik, bojovnik2: Bojovnik, kostka: Kostka) { self.bojovnik1 = bojovnik1 self.bojovnik2 = bojovnik2 self.kostka = kostka } func vykresli() { print("\n \n \n \n \n \n \n \n") print("-------------- Aréna -------------- \n") print("Zdraví bojovníků: \n") print("\(bojovnik1) \(bojovnik1.grafickyZivot())") print("\(bojovnik2) \(bojovnik2.grafickyZivot())") } private func vypisZpravu(_ zprava: String) { print(zprava) sleep(1) } func zapas() { // původní pořadí var b1 = bojovnik1 var b2 = bojovnik2 print("Vítejte v aréně!") print("Dnes se utkají \(bojovnik1) s \(bojovnik2)! \n") // prohození bojovníků let zacinaBojovnik2 = kostka.hod() <= kostka.vratPocetSten() / 2 if (zacinaBojovnik2) { b1 = bojovnik2 b2 = bojovnik1 } print("Začínat bude bojovník \(b1)! \nZápas může začít...") _ = readLine() // cyklus s bojem while b1.nazivu() && b2.nazivu() { b1.utoc(souper: b2) vykresli() vypisZpravu(b1.vratPosledniZpravu()) // zpráva o útoku vypisZpravu(b2.vratPosledniZpravu()) // zpráva o obraně if (b2.nazivu()) { b2.utoc(souper: b1) vykresli() vypisZpravu(b2.vratPosledniZpravu()) // zpráva o útoku vypisZpravu(b1.vratPosledniZpravu()) // zpráva o obraně } print(" ") } } } {/SWIFT}
{SWIFT} // vytvoření objektů let kostka = Kostka(pocetSten: 10) let zalgoren = Bojovnik(jmeno: "Zalgoren", zivot: 100, utok: 20, obrana: 10, kostka: kostka) let gandalf : Bojovnik = Mag(jmeno: "Gandalf", zivot: 60, utok: 15, obrana: 12, kostka: kostka, mana: 30, magickyUtok: 45) let arena = Arena(bojovnik1: zalgoren, bojovnik2: gandalf, kostka: kostka) // zápas arena.zapas() {/SWIFT}
{SWIFT} class Kostka : CustomStringConvertible { var description: String { return "Kostka s \(pocetSten) stěnami" } private var pocetSten : Int init() { pocetSten = 6 } init(pocetSten: Int) { self.pocetSten = pocetSten } func vratPocetSten() -> Int { return pocetSten } func hod() -> Int { return Int(arc4random_uniform(UInt32(pocetSten))) + 1 } } {/SWIFT}
Opět vidíme, jak můžeme znovupoužívat kód. S dědičností je spojeno opravdu mnoho technik, jak si ušetřit práci. V našem případě to ušetří několik řádků, ale u většího projektu by to mohlo mít obrovský význam.
Aplikace nyní funguje tak, jak má.
-------------- Aréna -------------- Zdraví bojovníků: Zalgoren [############# ] Gandalf [################# ] Gandalf použil magii za 52 hp Zalgoren utrpěl poškození 36 hp
Aréna nás však neinformuje o maně mága, pojďme to napravit. Přidáme
mágovi veřejnou metodu grafickaMana()
, která bude obdobně jako
u života vracet String
s grafickým ukazatelem many.
Abychom nemuseli logiku se složením ukazatele psát dvakrát, upravíme
metodu grafickyZivot()
v Bojovnik.swift
. Připomeňme
si, jak vypadá:
func grafickyZivot() -> String { var s = "[" let celkem : Double = 20 var pocet : Double = round((zivot / maxZivot) * celkem) if (pocet == 0) && (nazivu()) { pocet = 1; } for _ in 0..<Int(pocet) { s += "#" } s = s.padding(toLength: Int(celkem) + 1, withPad: " ", startingAt: 0) s += "]" return s }
Vidíme, že není kromě proměnných zivot
a
maxZivot
na životě nijak závislá. Metodu přejmenujeme na
grafickyUkazatel()
a dáme ji 2 parametry: aktuální hodnotu a
maximální hodnotu. Proměnné zivot
a maxZivot
v
těle metody poté nahradíme za aktualni
a maximalni
.
Modifikátor bude fileprivate
, abychom metodu mohli v potomkovi
použít:
fileprivate func grafickyUkazatel(aktualni: Double, maximalni: Double) -> String { var s = "[" let celkem : Double = 20 var pocet : Double = round((aktualni / maximalni) * celkem) if (pocet == 0) && (nazivu()) { pocet = 1; } for _ in 0..<Int(pocet) { s += "#" } s = s.padding(toLength: Int(celkem) + 1, withPad: " ", startingAt: 0) s += "]" return s }
Metodu grafickyZivot()
ve třídě Bojovnik
naimplementujeme znovu, bude nám v ní stačit jediný řádek a to zavolání
metody grafickyUkazatel()
s příslušnými parametry:
func grafickyZivot() -> String { return grafickyUkazatel(aktualni: zivot, maximalni: maxZivot) }
Určitě jsem mohl v tutoriálu s bojovníkem udělat metodu
grafickyUkazatel()
rovnou. Chtěl jsem však, abychom si ukázali,
jak se řeší případy, kdy potřebujeme vykonat podobnou funkčnost
vícekrát. S takovouto parametrizací se v praxi budete setkávat často,
protože nikdy přesně nevíme, co budeme v budoucnu od našeho programu
požadovat.
Nyní můžeme vykreslovat ukazatel tak, jak se nám to hodí. Přesuňme se
do třídy Mag
a naimplementujme metodu
grafickaMana()
:
func grafickaMana() -> String { return grafickyUkazatel(aktualni: mana, maximalni: maxMana) }
Jednoduché, že? Nyní je mág hotový, zbývá jen naučit arénu
zobrazovat manu v případě, že je bojovník mág. Přesuňme se tedy do
Arena.swift
.
Rozpoznání typu objektu
Jelikož se nám nyní vykreslení bojovníka zkomplikovalo, uděláme si na
něj samostatnou metodu vypisBojovnika()
, jejím parametrem bude
daná instance bojovníka:
func vypisBojovnika(_ b: Bojovnik) { print(b) print("Život:", terminator: " ") print(b.grafickyZivot()) }
Nyní pojďme reagovat na to, jestli je bojovník mág. Minule jsme si
řekli, že k tomu slouží operátor is
:
func vypisBojovnika(_ b: Bojovnik) { print(b) print("Život:", terminator: " ") print(b.grafickyZivot()) if b is Mag { print("Mana:", terminator: " ") print((b as! Mag).grafickaMana()) } }
Bojovníka jsme museli na mága přetypovat pomocí operátoru
as
, abychom se k metodě grafickaMana()
dostali.
Samotný Bojovnik
ji totiž nemá. Zas tu máme vykřičník
známý z Optional
. Funguje tu velmi podobně. Kdyby proměnná
b
nebyla na pozadí typu Mag
, tak program spadne. My
se ale nejdříve ptáme pomocí is
, jestli Mag
je a
až poté provedeme vynucené přetypování. Mohli bychom použít
?
, který by vrátil Optional
a my mohli výsledek
přetypování zpracovat bezpečně. Zde to ale není nutné a ani vhodné.
To bychom měli, vypisBojovnika()
budeme volat v metodě
vykresli()
, která bude vypadat takto:
{SWIFT} class Arena { private var bojovnik1 : Bojovnik private var bojovnik2 : Bojovnik private var kostka : Kostka init(bojovnik1: Bojovnik, bojovnik2: Bojovnik, kostka: Kostka) { self.bojovnik1 = bojovnik1 self.bojovnik2 = bojovnik2 self.kostka = kostka } func vykresli() { print("\n \n \n \n \n \n \n \n") print("-------------- Aréna -------------- \n") print("Zdraví bojovníků: \n") vypisBojovnika(bojovnik1) print(" ") vypisBojovnika(bojovnik2) } private func vypisZpravu(_ zprava: String) { print(zprava) sleep(1) } func zapas() { // původní pořadí var b1 = bojovnik1 var b2 = bojovnik2 print("Vítejte v aréně!") print("Dnes se utkají \(bojovnik1) s \(bojovnik2)! \n") // prohození bojovníků let zacinaBojovnik2 = kostka.hod() <= kostka.vratPocetSten() / 2 if (zacinaBojovnik2) { b1 = bojovnik2 b2 = bojovnik1 } print("Začínat bude bojovník \(b1)! \nZápas může začít...") _ = readLine() // cyklus s bojem while b1.nazivu() && b2.nazivu() { b1.utoc(souper: b2) vykresli() vypisZpravu(b1.vratPosledniZpravu()) // zpráva o útoku vypisZpravu(b2.vratPosledniZpravu()) // zpráva o obraně if (b2.nazivu()) { b2.utoc(souper: b1) vykresli() vypisZpravu(b2.vratPosledniZpravu()) // zpráva o útoku vypisZpravu(b1.vratPosledniZpravu()) // zpráva o obraně } print(" ") } } func vypisBojovnika(_ b: Bojovnik) { print(b) print("Život:", terminator: " ") print(b.grafickyZivot()) if b is Mag { print("Mana:", terminator: " ") print((b as! Mag).grafickaMana()) } } } {/SWIFT}
{SWIFT} class Bojovnik: CustomStringConvertible { fileprivate var jmeno : String fileprivate var zivot : Double fileprivate var maxZivot : Double fileprivate var utok : Int fileprivate var obrana : Int fileprivate var kostka : Kostka private var zprava : String = "" init(jmeno: String, zivot: Int, utok: Int, obrana: Int, kostka: Kostka) { self.jmeno = jmeno self.zivot = Double(zivot) self.maxZivot = self.zivot self.utok = utok self.obrana = obrana self.kostka = kostka } convenience init() { self.init(jmeno: "Standardní válečník", zivot: 100, utok: 20, obrana: 10, kostka: Kostka()) } var description: String { return jmeno } func nazivu() -> Bool { return zivot > 0 } fileprivate func grafickyUkazatel(aktualni: Double, maximalni: Double) -> String { var s = "[" let celkem : Double = 20 var pocet : Double = round((aktualni / maximalni) * celkem) if (pocet == 0) && (nazivu()) { pocet = 1; } for _ in 0..<Int(pocet) { s += "#" } s = s.padding(toLength: Int(celkem) + 1, withPad: " ", startingAt: 0) s += "]" return s } func grafickyZivot() -> String { return grafickyUkazatel(aktualni: zivot, maximalni: maxZivot) } func utoc(souper: Bojovnik) { let uder = utok + kostka.hod() nastavZpravu("\(jmeno) útočí s úderem za \(uder) hp") souper.branSe(uder: uder) } func branSe(uder: Int) { let zraneni = Double(uder - (obrana + kostka.hod())) var zprava = "" if (zraneni > 0) { zivot -= zraneni zprava = "\(jmeno) utrpěl poškození \(Int(zraneni)) hp" if (zivot <= 0) { zivot = 0 } } else { zprava = "\(jmeno) odrazil útok" } nastavZpravu(zprava) } fileprivate func nastavZpravu(_ zprava: String) { self.zprava = zprava } func vratPosledniZpravu() -> String { return zprava } } class Mag: Bojovnik { private var mana : Double private var maxMana : Double private var magickyUtok : Int init(jmeno: String, zivot: Int, utok: Int, obrana: Int, kostka: Kostka, mana: Int, magickyUtok: Int) { self.mana = Double(mana) self.maxMana = self.mana self.magickyUtok = magickyUtok super.init(jmeno: jmeno, zivot: zivot, utok: utok, obrana: obrana, kostka: kostka) } override func utoc(souper: Bojovnik) { var uder = 0 // Mana není naplněna if mana < maxMana { mana += 10; if (mana > maxMana) { mana = maxMana } super.utoc(souper: souper) nastavZpravu("\(jmeno) útočí s úderem za \(uder) hp") } else { // Magický útok uder = magickyUtok + kostka.hod() nastavZpravu("\(jmeno) použil magii za \(uder) hp") mana = 0 } souper.branSe(uder: uder) } func grafickaMana() -> String { return grafickyUkazatel(aktualni: mana, maximalni: maxMana) } } {/SWIFT}
{SWIFT} // vytvoření objektů let kostka = Kostka(pocetSten: 10) let zalgoren = Bojovnik(jmeno: "Zalgoren", zivot: 100, utok: 20, obrana: 10, kostka: kostka) let gandalf : Bojovnik = Mag(jmeno: "Gandalf", zivot: 60, utok: 15, obrana: 12, kostka: kostka, mana: 30, magickyUtok: 45) let arena = Arena(bojovnik1: zalgoren, bojovnik2: gandalf, kostka: kostka) // zápas arena.zapas() {/SWIFT}
{SWIFT} class Kostka : CustomStringConvertible { var description: String { return "Kostka s \(pocetSten) stěnami" } private var pocetSten : Int init() { pocetSten = 6 } init(pocetSten: Int) { self.pocetSten = pocetSten } func vratPocetSten() -> Int { return pocetSten } func hod() -> Int { return Int(arc4random_uniform(UInt32(pocetSten))) + 1 } } {/SWIFT}
Hotovo
-------------- Aréna -------------- Zdraví bojovníků: Zalgoren Život: [########## ] Gandalf Život: [##### ] Mana: [############# ] Zalgoren útočí s úderem za 28 hp
Pokud jste něčemu nerozuměli, zkuste si článek přečíst vícekrát nebo pomaleji, jsou to důležité praktiky.
V následujícím cvičení, Řešené úlohy k 5.-8. lekci OOP ve Swift, si procvičíme nabyté zkušenosti z předchozích lekcí.
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 8x (26.19 kB)
Aplikace je včetně zdrojových kódů v jazyce Swift