IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
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í.

7. diel - Dokončenie kolízií vo SpriteKit

V minulej lekcii, Pridanie fyziky a detekcia kolízií vo SpriteKit , sme skončili pri metódy didBegin(), ktorá nás informuje o kolízii medzi objektmi, pre ktoré sme notifikácia zapli. Dnes všetko sprevádzkujeme, takže hráč bude môcť konečne ničiť nepriateľov.

Zistenia konfliktu s už existujúcim objektu

Od SpriteKit dostaneme v metóde didBegin() parameter contact typu SKPhysicsContact, ktorý nás informuje o tom, aké dva objekty kolidovali. Tradičné implementácia tejto metódy v prvom rade vytiahne obaja SKNode objekty pomocou guard:

guard let nodeA = contact.bodyA.node else { return }
guard let nodeB = contact.bodyB.node else { return }

Vďaka tomu vieme, že môžeme kolízii spracovať, pretože máme oba objekty. Lenže, čo z toho je raketa? Laser? Loď hráča?

Názvy objektov

Všetky SKNode majú vlastnosť name, ktorá nám v tomto pomôže. Vrátime sa teda najskôr do triedy Player a v init() nastavíme vlastnosť name:

name = "player"

Podobne si nastavíme rakety:

let missile1 = SKSpriteNode(imageNamed: "playerMissile")
missile1.name = "missile"
let missile2 = missile1.copy() as! SKSpriteNode

Využívame kopírovanie, takže meno stačí nastaviť iba raz.

Naša hra má dosť jednoduché kolízie a ak by sme zistili, že jeden objekt je loď hráča, tak druhý musí byť logicky laser. Napriek tomu odporúčam pomenovať si poctivo všetky objekty, nech je kód vo výsledku čitateľnejší.

Zostáva nastaviť mená v triede Enemy:

enemy.name = "enemy"

A laser:

laser.name = "laser"

DidBegin ()

Po krátkej odbočke sa môžeme vrátiť do metódy didBegin(). Spracovanie kolízie rozdelíme a najskôr vyriešime situáciu, kedy raketa trafí nepriateľa.

Pre prehľadnosť si pripravíme metódu, ktorá sa postará práve o túto situáciu. V didBegin() ju teda stačí iba správne poslať oba objekty a bod, kde došlo ku kontaktu:

func missileHit(_ missile: SKNode, enemy: SKNode, at point: CGPoint) {
}

A zistenie, aký z objektov je raketa a aký nepriateľ, vyzerá potom nasledovne:

if nodeA.name == "missile" || nodeB.name == "missile" {
    if nodeA.name == "enemy" {
        missileHit(nodeB, enemy: nodeA, at: contact.contactPoint)
    } else {
        missileHit(nodeA, enemy: nodeB, at: contact.contactPoint)
    }
}

Zásah

Poďme na kolíziu nejako reagovať. V metóde missileHit() pre začiatok pri kolízii odstránime oba objekty zo scény:

missile.removeFromParent()
enemy.removeFromParent()

Pole enemies

Lenže nepriateľov máme uložené tiež v poli enemies, ktoré slúžia pre ľahký prístup k nim, keď chceme, aby strieľali. Máme na výber, buď sa starať o to, aby sme zasiahnuté nepriateľov odstránili aj z poľa, alebo to vymyslieť lepšie, aby pole nebolo potrebné.

GameScene ponúka vlastnosť children obsahujúce všetky objekty v scéne. My ale používame enemyAnchor pre jednoduchú animáciu pohybu nepriateľov, takže už je vlastne v jednej kolekcii máme.

Jednoducho teda premennú enemies zmažeme. Ďalej je potrebné upraviť createEnemyWave() a odstrániť pridávanie nových nepriateľov do už zmazaného poľa. Posledný nutnou úpravou je metóda enemyFireTimerTick().

Namiesto pole enemies budeme cez for cyklus iterovat enemyAnchor.children, ktorý ale vracia pole typu [SKNode]. Je hromada spôsobov, ako sa s týmto vysporiadať. Mohli by sme napríklad pretypovanie vykonávať cez guard alebo if let v tele cykle.

My ale využijeme silné možnosti, tzv. Pattern matching, pomocou case let zápisu. Rovno si ho ukážeme celý:

for case let child as Enemy in enemyAnchor.children

Ak vás for case let za sebou vydesilo, ani sa nevidím. Prvýkrát som na tento zápis pozeral dosť neveriacky. Postará sa o to, aby lokálna premenná child bola typu Enemy a my s ňou mohli pracovať ako predtým. Samozrejme je nutné premenovať skorší lokálnu premennú enemy v tele cyklu na child.

Pomocou case let môžete vo for cykle jednoducho rozbaľovať Optional hodnoty, stačilo by namiesto child napísať child?. Ak by sme pracovali s poľom Optional hodnôt, v tele cyklu by už Optional nebolo. Rovnako tak sa case let dá využiť v konštrukcii switch.

Hru môžeme zapnúť a presvedčiť sa, že rakety konečne likvidujú protivníkov:

Kolízia rakiet s protivníkmi v iOS hre vo Swift - Tvorba iOS hier vo Swift

Oprava kolízií

Hádam, že ste si všimli, že kolízie fungujú divne pri zásahu rakety. Niekedy dokonca akoby raketa ignorovala nepriateľa a letela ďalej.

SpriteKit nám ponúka pomerne jednoduchú možnosť, ako overiť nastavenie fyzikálne reprezentácie pre objekty. Prejdeme teda do GameViewController.swift a v metóde viewDidLoad() nájdeme riadok view.showsNodeCount = true.

Ako prezrádza názov, určuje, či má scéna zobrazovať počet objektov. To je pre prípad, aby sme si overili, že korektne odoberáme tie už nepotrebné. Pridáme ďalší riadok a to:

view.showsPhysics = true

Ide o ďalší debug konfigurácii pre zobrazenie fyziky. Aktuálne nastavené fyzikálne reprezentácie objektov budú zobrazené bledomodrým obrysom. Zapneme hru a okamžite vidíme, kde je problém:

Debugging fyziky vo SpriteKit - Tvorba iOS hier vo Swift

Ako sami vidíte, fyzikálne reprezentácie a vykreslenie nepriateľov nesedí, zvyšok funguje dobre. Problém je v nastavení anchorPoint pre naše nepriateľov. Neuvedomil som si v predchádzajúcej lekcii, že ide iba o vizuálne nastavenie vykresľovania a neskôr nastane problém s fyzikou. Tak sme si aspoň mohli ukázať, ako problémy hľadať.

Zmena anchorPoint

Ak by sme vytvárali fyzikálne reprezentáciu ako obdĺžnik alebo kruh, išlo by posunúť stred a tým problém vyriešiť. My ale používame textúru, takže nám nezostáva nič iné, než nastavenie anchorPoint vrátiť späť a upraviť kód generovanie vlny nepriateľov, aby boli na stredu.

Úpravy teda vykonáme v metóde createEnemyWave(). Najskôr odstránime nastavenie anchorPoint a potom upravíme počítanie xPosition v tele cykle takto:

let xPosition = xOffset + CGFloat(i) * (enemySize + enemySpacing) + enemySize / 2

Vlastne sme pridali pripočítanie polovice šírky nepriateľa, ako kompenzáciu za upravený anchorPoint.

Môžeme skúsiť:

Opravené kolízie v iOS hre vo SpriteKit - Tvorba iOS hier vo Swift

Teraz už budú kolízie fungovať podľa očakávania :-) V budúcej lekcii, Ďalšie časticové efekty vo SpriteKit , pridáme časticové efekty explózie a lasera :)


 

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

 

Predchádzajúci článok
Pridanie fyziky a detekcia kolízií vo SpriteKit
Všetky články v sekcii
Tvorba iOS hier vo Swift
Preskočiť článok
(neodporúčame)
Ďalšie časticové efekty 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