Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

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:

Tvorba iOS hier vo Swift
Tvorba iOS hier vo Swift
Tvorba iOS hier vo Swift

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ť:

Tvorba iOS hier vo Swift

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).

Tvorba iOS hier vo Swift

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.

Vytvorenie Segue z tlačidla na druhú obrazovku - Tvorba iOS hier vo Swift

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:

Herná menu pre SpriteKit iOS hru vo Swift - Tvorba iOS hier vo Swift

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.

Tvorba iOS hier vo Swift

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

 

Predchádzajúci článok
Pridanie Parallax efektu a životov hráča vo SpriteKit
Všetky články v sekcii
Tvorba iOS hier vo Swift
Preskočiť článok
(neodporúčame)
Nekonečné vlny nepriateľov a ich animácie vo SpriteKit
Článok pre vás napísal Filip Němeček
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Autor se věnuje vývoji iOS aplikací (občas macOS)
Aktivity