11. diel - Nekonečné vlny nepriateľov a ich animácie vo SpriteKit
V predchádzajúcej lekcii, Poškodenie hráča, menu hry a reštart vo SpriteKit , sme si ukázali okrem iného ako implementovať menu hry a pridali reštart, ak hráča nepriatelia zničí. Hráč ale stále môže skôr zničiť vlnu nepriateľov a potom už nerušene pokračovať v hre, pretože sa ďalej nič nestane.
Na dokončenie hry nám zostáva upraviť, ako fungujú vlny nepriateľov,
aby sa po zničení objavila nová. Pripravíme si novú metódu
createEnemies(), ktorá sa bude starať o vytvorenie všetkých
nepriateľov a tiež animáciu ich príletu na scénu.
Náhodné formácie nepriateľov
Aktuálne používame metódu createEnemyWave(), ktorá vytvorí
vlnu nepriateľov podľa zadaného počtu. My by sme však potrebovali, aby sa
vlny od seba nelíšili len počtom nepriateľov, ale aj formácií, v ktorej
letí.
createEnemyWave()
Najprv upravíme createEnemyWave(), aby brala do úvahy rady
nepriateľov. Pridáme teda parameter row typu
Int:
func createEnemyWave(enemyCount: Int, row: Int)
Po úprave tiež zmažeme volanie metódy v
didMove(), nech na nás Xcode nekričí, že nesedí parametre.
A vnútri metódy nad cyklom vypočítame pozíciu y:
let yPosition: CGFloat = CGFloat(row) * 120
Tú potom nastavíme nepriateľom v tele cyklu:
newEnemy.position = CGPoint(x: xPosition, y: yPosition)
Na záver pridáme náhodný výber typu nepriateľov upravením volanie
Enemy.create() na začiatku metódy:
let enemyTemplate = Enemy.create(variant: Int.random(in: 1...3))
createEnemies()
A teraz sa môžeme pustiť do metódy createEnemies():
func createEnemies() { let rows = Int.random(in: 2...3) for row in 1...rows { createEnemyWave(enemyCount: Int.random(in: 3...5), row: row) } }
Odmenou za celkom zložitú metódu createEnemyWave() je jej
jednoduché použitie, keď chceme podľa náhody pripraviť dve alebo tri rady
nepriateľov a v každej mať v rozmedzí od 3 do 5
nepriateľských lodí.
setupEnemyAnchor()
Ešte musíme upraviť metódu setupEnemyAnchor(), konkrétne
pozícii, aby bolo miesto na tri rady nepriateľov:
enemyAnchor.position = CGPoint(x: 0, y: size.height - 550)
Môžeme vyskúšať:
Každé zavolanie createEnemies() vytvorí náhodnú
nepriateľskú formáciu:-)
Generovanie nových nepriateľov
Teraz stačí pridať animáciu prílete a generovať nové nepriateľov, keď hráč všetky zlikviduje.
IsGameInProgress
Musíme hlavne vyriešiť, aby nepriatelia nestrieľali, pokiaľ práve
animuje ich prílet. Mohli by sme si vytvoriť ďalšie bool
premennú. Prehľadnejšie a jednoduchšie bude ale upraviť súčasnú
premennú isGameOver, ktorú premenujeme na všeobecnejšie
isGameInProgress.
Zo začiatku bude nastavená aj na false a vždy ju nastavíme
na true, keď nepriatelia priletia. Na false ju opäť
nastavíme v prípade, keď hráč stratí všetky životy alebo zlikviduje
všetkých nepriateľov. Začneme teda s premenovaním:
var isGameInProgress = false
lives
Upravíme didSet blok premennej lives:
if lives < 0 { isGameInProgress = false controller?.showGameOver() return }
didBegin()
Ďalšia úpravu je potrebné vykonať na začiatku metódy
didBegin():
guard isGameInProgress else { return }
enemyFireTimerTick()
A konečne v metóde enemyFireTimerTick(), kde na začiatok
pridáme to isté:
guard isGameInProgress else { return }
To isté by sme mohli urobiť v metóde playerFireTimerTick(),
ale vďaka kontrole v didBegin() sú rovnako rakety hráča
neškodné. Opäť je to na vás.
enemyCount
Aby sme mohli zistiť, či už boli zničení všetci nepriatelia, pridáme
si vlastnosť enemyCount, ktorá sa jednoducho opýta na potomkov
enemyAnchor:
var enemyCount: Int { return enemyAnchor.children.count }
missileHit()
Zostáva pridať kontrolu na koniec metódy missileHit(). Tu ale
pozor. Nemôžeme sa na konci jednoducho opýtať na enemyCount,
pretože nepriateľov ničíme najprv fadeOut akciou, ktorá
nejaký čas trvá. Na enemyCount by sme sa teda spýtali skôr,
než dôjde k zničeniu nepriateľa.
Zistil som, že na tomto riadku:
let fadeOut = SKAction.fadeOut(withDuration: 0.2)
Som mal preklep a použil som akciu SKAction.fadeIn() s
opačným efektom, tak si to prosím opravte a ospravedlňujem sa za
komplikácie:-)
Späť k nášmu problému. Vytvoríme si všeobecnú SKAction,
ktorá skontroluje počet nepriateľov:
let createNewEnemiesIfNeeded = SKAction.run { if self.enemyCount == 0 { self.isGameInProgress = false self.createEnemies() } }
A potom ju len pridáme na koniec existujúcej sekvencie:
let sequence = SKAction.sequence([fadeOut, SKAction.removeFromParent(), createNewEnemiesIfNeeded])
A máme nekonečné nepriateľov. Zostáva ich animácie.
Animácie vlny nepriateľov
Upravíme metódu createEnemies(), aby sa nepriatelia jednoducho
neobjavili, ale namiesto toho prileteli. Na jej začiatok teda pridáme
posunutie enemyAnchor mimo viditeľnú scénu a odstránenie
všetkých akcií, pretože pre nepriateľov konfigurujeme nekonečný pohyb do
strán. Takto by nám akurát rozhadzoval naše chystanej animácie.
enemyAnchor.position = CGPoint(x: 0, y: size.height + 700) enemyAnchor.removeAllActions()
Ešte nezabudnite z metódy didMove() odstrániť volanie
startEnemyMovement(). Rovnako tak môžeme prečistiť
setupEnemyAnchor() a odstrániť nastavenie pozície:
enemyAnchor.position = CGPoint(x: 0, y: size.height - 550) // není již třeba
Posunutie pozície enemyAnchor vyššie by nám malo poskytnúť
dosť priestoru na vytvorenie nepriateľov, bez toho aby boli vidieť.
Pod existujúcim for cyklom si pripravíme animácie a ďalšie
SKAction:
let shrink = SKAction.scale(to: 0.7, duration: 0) let moveIntoView = SKAction.moveTo(y: size.height - 550, duration: 3) let resetSize = SKAction.scale(to: 1, duration: 3) let group = SKAction.group([moveIntoView, resetSize]) let startGame = SKAction.run { self.isGameInProgress = true self.startEnemyMovement() }
SKAction je veľa, ale nejedná sa o nič komplikovaného:-)
Najskôr zmenšíme vytvorené nepriateľov a potom ich zároveň pomocou
SKAction.group posunieme do viditeľnej scény a zväčšíme na
pôvodnú veľkosť.
Potom už len cez SKAction.run spustíme hru, vrátane pohybu
nepriateľov.
Môžeme vyskúšať:
A tým je naša vesmírna strieľačka Galaxy Invaders vo SpriteKit hotová. Ďakujem za záujem!:)
V tutoriále som sa snažil ukázať čo možno najviac techník a možností, ktoré nám SpriteKit ku tvorbe 2D hier pre iOS poskytuje. Ukázali sme si základnú prácu s textúrami a hernými objekty, celkom dosť sa venovali časticovým efektom, zapracovali sme kolízie pomocou fyziky a mnoho ďalšieho.
Možná vylepšenia
Do hry je toho možné samozrejme ešte kopu pridať. A či sa hrám plánujete venovať, rozhodne by som odporučil skúsiť si Galaxy Invaders čo najviac rozšíriť. Predsa len experimentovaním a úpravami sa môžete veľa naučiť.
Napríklad môžete pridať ďalšie zvukové efekty alebo napríklad hudbu
na pozadí pomocou SKAudioNode, ktorú stačí len vytvoriť a
pridať do scény. O prehrávanie audia sa postará sama a funguje v nekonečnej
slučke.
Vlny nepriateľov aktuálne generujeme, lepšie by bolo nadefinovať ich a ponúknuť hráči, aby postupoval v úrovniach. Podľa danej úrovne môžete napríklad aj meniť, ako často nepriatelia strieľa. Rovnako tak by nebolo ťažké pridať viac typov pohybov nepriateľov a treba medzi nimi náhodne vyberať alebo skúsiť ponúknuť hráči súboj s jedným veľkým nepriateľom, ktorého nezničí jeden zásah, takže u neho bude treba ukladať poškodenia a napríklad aj zmeniť jeho bojový arzenál.
Ďalej môžete pridať napríklad power-upy, ktoré hráč pomocou kolízie zoberie. Napríklad rýchlejší frekvenciu streľby, doplnenie životov alebo zobratie štítu, ktorý balíček od Kenney.nl obsahuje.
A určite sami vymyslíte veľa ďalších možností, ako hru vylepšiť.
Nezabudnite, že svoje výsledné výtvory môžete nahrať na ITnetwork a trebárs ostatné inšpirovať alebo im ukázať, ako ďalšie rozšírenie implementovať.
Ak si nebudete vedieť s niečím rady, určite sa nebojte napísať do komentárov, na tunajšie fórum (kľudne ma môžete v príspevku označiť, nech o ňom viem) alebo do súkromných správ. Rád pomôžem, ak to bude v mojich silách.
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é 18x (1.73 MB)
Aplikácia je vrátane zdrojových kódov v jazyku Swift
