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é 17x (1.73 MB)
Aplikácia je vrátane zdrojových kódov v jazyku Swift