10. diel - Poškodenie hráča, menu hry a reštart vo SpriteKit
Dnes sa budeme venovať poškodenia lode, hernému menu a reštarte hry.
Poškodenie hráča
Najprv si ukážeme, ako simulovať poškodenie.
Textúry
Balíček textúr od Kenney.nl obsahuje aj varianty poškodenia pre
jednotlivé lode hráča. Jedná sa vlastne o obrázky, ktorými prekryjeme
hlavné textúru a tým bude poškodenie vidieť. Ďalšou možnosťou by bolo
mať kompletný obrázky poškodenej lode a meniť vlastnosť
texture
u SKSpriteNode
.
Prekryvné obrázky poškodenia lode vyzerajú takto a na lodi budú indikovať koľko ju ešte zostáva životov:
ApplyDamage ()
V triede Player
si pridáme metódu applyDamage()
.
Tá bude ako parameter očakávať zostávajúce životy, takže v našom
prípade buď 2
, 1
alebo 0
. Ešte pred
implementáciou si pripravíme inštanciu SKSpriteNode
pre prvý
poškodenie:
private let damageNode = SKSpriteNode(imageNamed: "playerShip2_damage1")
A metódu implementujeme:
switch livesLeft { case 3: break case 2: damageNode.zPosition = 5 addChild(damageNode) case 1: damageNode.texture = SKTexture(imageNamed: "playerShip2_damage2") case 0: damageNode.texture = SKTexture(imageNamed: "playerShip2_damage3") default: assertionFailure("Invalid parameter") }
Pri prvom zásahu pridáme poškodenia do scény, aby prekrylo loď hráča a pri ďalších len upravíme textúru poškodenia.
Vetva default
sme vyriešili s pomocou šikovné funkcie
assertionFailure()
, aby sme boli upozornení v prípade, že
odniekiaľ pošleme neplatný parameter. case 3
je tu z dôvodu,
že metódu budeme volať cez didSet
v GameScene
a na
začiatku nastavujeme lives
na 3
, aby sa propsal text
tiež do SKLabelNode
.
Spoločne s assert()
sa jedná o skvelé
pomocníkmi pri vývoji, môžete sa pomocou nich totiž uisťovať, že je
program v takom stave, v akom očakávate. assert()
totiž ako
prvý parameter očakáva podmienku. V release buildu tieto funkcie vôbec nie
sú, takže používateľom aplikácie / hra nespadne, ak sa stane niečo
neočakávané.
Zavolanie metódy
Upravíme teda premennú lives
a pri zmene nastavíme
poškodení hráči:
var lives: Int = 3 { didSet { livesLabel.text = "LIVES: \(lives)" player.applyDamage(livesLeft: lives) } }
A hru môžeme vyskúšať:
Menu
Určite budete súhlasiť, že vrhnúť hráča po zapnutí hry hneď do boja nie je úplne ideálne. Chcelo by to nejaké menu.
Menu by sme mohli skúsiť urobiť cez SpriteKit, avšak ponúka sa
jednoduchšie možnosť. Síce sme pracovali v posledných dieloch výhradne v
GameScene
, ale na pozadí je UIViewController
, v
našom prípade pomenovaný GameViewController
, a tradičné UIKit
aplikácie s Main.storyboard
.
Nový View Controller
Hlavné menu teda vytvoríme ako obyčajnú UIKit obrazovku. Táto časť sa
netýka SpriteKit, takže ju prejdeme v rýchlosti. Práca s UIKit ich prípadne
detailne popísaná v kurze Vyvíjame iOS aplikácie vo
Swift. Do Main.storyboard
pretiahneme nový
View Controller
a nastavíme ho ako initial (zaškrtnutím "Is
Initial View Controller", spoznáte to tiež podľa šípky ukazujúce na túto
obrazovku).
Bude nám stačiť pozadia (klasický UIImageView
), ďalší
obrázok pre logo, ktoré som pripravil v Affinity Photo a konečne tlačidlo
(UIButton
), z ktorého pretiahneme segue (za držanie klávesy
Control, pozri ukážku nižšie) rovno na druhú obrazovku. Tu som
zvolil "Present Modally" a "Full Screen". Vlastne ani nepotrebujeme triedu pre
túto obrazovku.
UIImageView
pre pozadie som pomocou AutoLayout nastavil
0
od všetkých hrán (priamo k SuperView, takže je nutné
odškrtnúť margin a ignorovať Safe Area). Nezabudnite nastaviť Content Mode
na "Aspect Fill", aby obrázok zaplnil celé okno. UIImageView
s
logom hry stačí vycentrovať a nastaviť mu napríklad 50
bodov
od hornej hrany. Tlačidlo opäť vycentrujeme a nastavíme mu vertikálne
odsadenie od loga. Tým je layout hotový.
Výsledok vyzerá takto:
SpriteKit menu by fungovalo tak, že by sme napr. Vytvorili
novú scénu, ktorá by mala tieto isté prvky (pomocou
SKSpriteNode
) a po detekcii výbere play by sme pomocou SpriteKit
prepli na inú scénu. Výhodou tohto postupu by bolo, že by sme napríklad
mohli dosiahnuť efekt, že logo "odletí" preč a namiesto neho priletia prvá
vlna nepriateľov.
Vrátenie hráča späť do menu
Teraz potrebujeme spôsob, ako hráča vrátiť späť do menu. Pre zjednodušenie zobrazíme dialóg len keď dôjde k jeho zabitie nepriateľmi. V skutočnej hre by sme mali pre tieto potreby ešte ďalšie tlačidlo.
Zobrazenie dialógu môžeme vyriešiť cez
UIAlertViewController
, ale ten nevytvoríme z hernej scény.
Potrebujeme teda vyriešiť komunikáciu medzi scénou a
GameViewController
. Najjednoduchšie bude držať si referenciu v
scéne pre ľahký prístup. Tu ale pozor, pretože referencie musí byť
weak
. View controller si totiž už drží silnú referenciu hernej
scény, takže by inak došlo k retain cycle.
weak var controller: GameViewController?
GameViewController
Prejdeme do GameViewController
a upravíme
viewDidLoad()
. Už nám nestačí získať akúkoľvek hernú
scénu, takže pridáme pretypovanie na koniec if let
a nastavenie
referencie:
if let scene = SKScene(fileNamed: "GameScene") as? GameScene { // Set the scale mode to scale to fit the window scene.scaleMode = .aspectFill scene.controller = self ..
A pridáme metódu pre zobrazenie konca hry:
func showGameOver() { let ac = UIAlertController(title: "Game over!", message: nil, preferredStyle: .alert) ac.addAction(UIAlertAction(title: "Retry", style: .default, handler: { (_) in // doplníme })) ac.addAction(UIAlertAction(title: "Menu", style: .cancel, handler: { [weak self] (_) in self?.dismiss(animated: true, completion: nil) })) present(ac, animated: true) }
GameScene
Zvyšok vyriešime v GameScene
. Pridáme si premennú pre
stráženie, či náhodou hra neskončila:
var isGameOver = false
Ak áno, tak deaktivujeme kolízie, respektíve reagovanie na ne hneď na
začiatku metódy didBegin()
:
guard !isGameOver else { return }
A zostáva pridať logiku na začiatok bloku didSet
premenné
lives
:
if lives < 0 { isGameOver = true controller?.showGameOver() return }
Akonáhle hráč stratí všetky životy, zobrazíme náš dialóg, ktorý zatiaľ funguje len pre vrátenie sa do menu.
Reštart hry
Zostáva nám doriešiť situáciu, kedy užívateľ zvolí "Retry" voľbu v dialógu. V takom prípade chceme hru resetovať.
Mohlo by vás napadnúť jednoducho vrátiť späť počet životov, znova vytvoriť nepriateľov, odstrániť poškodenia a tak podobne. To je ale zbytočne veľa práce a riskujete, že na niečo zabudnete. Oveľa lepšie je jednoducho zobraziť novú inštanciu scény, SpriteKit nám k tomu poskytne aj pekné animácie.
Takže sa vrátime späť do GameViewController
a vytvoríme tu
metódu restartGame()
. Ďalej si pridáme ešte metódu
createGameScene()
, nech neduplikuje kód z
viewDidLoad()
.
CreateGameScene ()
Kód metódy je nasledovné:
func createGameScene() -> GameScene? { if let scene = SKScene(fileNamed: "GameScene") as? GameScene { // Set the scale mode to scale to fit the window scene.scaleMode = .aspectFill scene.controller = self return scene } return nil }
Väčšina metódy pochádza z viewDidLoad()
, kam behom
chvíľky doplníme nové vytvorenie scény za pomoci novo vytvorené metódy
createGameScene()
.
Logika je na jednom mieste a môžeme ju neskôr rozšíriť a nebáť sa,
že treba reštart hry bude fungovať inak ako jej prvotné zapnutia. Samozrejme
ešte upravíme vnútro viewDidLoad()
:
if let scene = createGameScene() { // Present the scene view.presentScene(scene) }
RestartGame ()
A môžeme sa vrátiť k restartGame()
a vytvoriť novú scénu
s animovaným prechodom:
func restartGame() { if let scene = createGameScene() { let transition = SKTransition.flipHorizontal(withDuration: 1) skView.presentScene(scene, transition: transition) } }
SKTransition
ponúka veľa efektov, takže sa rozhodne nebojte
experimentovať. Pretože potrebujeme scénu prezentovať z SKView
a nie z UIView
(ktorý je v skutočnosti SKView
),
pripravíme si vlastnosť pre jednoduché získanie:
var skView: SKView { return view as! SKView }
Samozrejme nesmieme zabudnúť restartGame()
zavolať z
dialógu, na mieste, kde som nechal komentár // doplníme
.
restartGame()
Môžete vyskúšať, že voľba "Retry" funguje korektne:-)
Zostáva nám už len posledný lekcie, Nekonečné vlny nepriateľov a ich animácie vo SpriteKit , kde upravíme vlny nepriateľov a povieme si, ako by sa hra dala ešte vylepšiť.
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é 6x (1.07 MB)
Aplikácia je vrátane zdrojových kódov v jazyku Swift