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

Lekce 6 - Přidání fyziky a detekce kolizí ve SpriteKit

V předchozí lekci, Nepřátelé střílí zpět a dokonce laserem ve SpriteKit, jsme v naší iOS hře ve Swift skončili vyzbrojením nepřátel laserem a přidali zvukové efekty.

Naše střely pořád nic nedělají a právě to napravíme v tomto tutoriálu.

Fyzika ve SpriteKit

Je na čase představit si, jak funguje fyzika ve SpriteKit. Nám pomůže s detekcí kolizí, ale využít se dá pro řadu dalších věcí, protože například můžete velmi snadno upravovat gravitaci herního světa. Ta je ve výchozím stavu nastavena tak, aby emulovala Zemi, takže předměty, které mají fyzické tělo, automaticky padají k zemi a odrážejí se od sebe.

Pomocí gravitace můžete snadno implementovat ovládání ve hrách pomocí naklánění zařízení. Jednoduše podle naklonění upravíte gravitaci a SpriteKit se postará o zbytek.

Aktivace fyziky

Fyzika již na pozadí naší hry funguje, ale žádný z našich objektů nemá nastavené physicsBody, takže se na něj nevztahuje. Jako první tedy musíme nastavit physicsBody pro všechny naše objekty, abychom mohli později detekovat kolize těchto objektů.

Player

Začneme třeba u hráče (soubor Player.swift) a na konec init() přidáme tento řádek:

physicsBody = SKPhysicsBody(texture: texture, size: texture.size())

To stačí, aby fyzika začala na model hráče realisticky působit. Fyzikální reprezentaci vytváříme z textury, což je sice náročnější na výkon, ale budeme mít tzv. "pixel-perfect" kolize. Laserová střela se bude muset skutečně přesně dotknout lodi hráče, aby byla započítána.

Ještě ve třídě Player zůstaneme a rovnou nastavíme physicsBody také raketám v metodě createMissiles():

missile1.physicsBody = SKPhysicsBody(rectangleOf: missile1.size)
missile2.physicsBody = SKPhysicsBody(rectangleOf: missile2.size)

Pro ukázku jsem zde použil vytvoření fyzikální reprezentace jako obdélníku, což je při detekci kolizí mnohem rychlejší než vytvoření z textury. Výkonově by nebyl problém opět použít texturu, chci ale ukázat také další způsob :-) V momentě, kdy budete mít ve hře hromadu fyzikálních objektů, se již vyplatí přemýšlet, jakým stylem physicsBody vytvořit.

Enemy

Přesuneme se do třídy Enemy a provedeme zde prakticky to stejné. Nejprve v metodě create():

enemy.physicsBody = SKPhysicsBody(texture: texture, size: texture.size())

A pro laserovou střelu:

laser.physicsBody = SKPhysicsBody(rectangleOf: laser.texture!.size())

laser.texture je Optional, protože SKSpriteNode jde vytvořit pouze z barvy bez textury. My ale víme, že jsme texturu použili a vykřičník tak není problém.

Zkouška fyziky

Nyní můžete hru vyzkoušet a podívat se, jak se vlastně změnila.

Pokud jste vše nastavili správně, viděli jste názornou ukázku simulované gravitace. My samozřejmě nechceme, aby všechny lodě spadly mimo hrací plochu...

Vypnutí gravitace

Fyziku v naší vesmírné hře sice využijeme, ale gravitace zde nedává moc smysl. Takže ji v didMove() vypneme:

physicsWorld.gravity = .zero

Vlastnost gravity je typu CGVector, který se používá pro reprezentaci směru a intenzity. Ve výchozím stavu je gravitace nastavena na vektor CGVector(dx: 0, dy: -9.8).

Hru můžete zapnout a radovat se, že naše objekty nepadají nekonečně dolů. Jenže teď do sebe vše naráží a různě se otáčí. To hned vyřešíme pomocí bitové masky, až budeme kolize konfigurovat.

Druhou možností by bylo nastavit vlastnost physicsBody.isDynamic, která zabrání, aby se objekt hýbal, ať již vlivem gravitace nebo kolize. Zde vlastnost nevyužijeme, hodí se ale vědět, k čemu slouží. Dejte zároveň pozor na to, že kolize nebudou fungovat, pokud budou mít oba objekty nastavené idDynamic na false.

Konfigurace kolizí

Nám vlastně bude stačit, aby nám SpriteKit řekl, když dojde ke kolizi mezi raketou a nepřítelem a také ke kolizi mezi laserem a lodí hráče. Pro konfiguraci budeme potřebovat kategorii kolidovaného objektu. Mohli použít obyčejná čísla, připravíme si ale enum, ať je výsledek přehlednější.

Enum

Vytvoříme si tedy enum pojmenovaný třeba ObjectType v souboru GameScene.swift, ale mimo definici třídy GameScene:

enum ObjectType: UInt32 {
    case player = 1
    case missile = 2
    case enemy = 4
    case laser = 8
}

Jako typ jsme použili UInt32, protože právě ten používá SpriteKit pro definici masek. Hodnota je vždy dvojnásobek předchozího, aby šlo tyto hodnoty pro danou masku kombinovat (když si představíte čísla binárně, je player 0001, missile 0010, enemy 0100 a laser 1000, player a zároveň laser by tedy teoreticky mohl být 1001, i když to zde nedává smysl). Kombinací bychom využili, pokud bychom třeba chtěli, aby docházelo ke kolizi mezi hráčem a laserem a také mezi hráčem a nepřítelem, což v naší hře ale nenastane.

Teď musíme tyto hodnoty správně přiřadit našim objektům. K tomu si ale konečně představme jak bitové masky ve SpriteKit fungují.

Vysvětlení bitových masek

Objekty ve SpriteKit mají bitové masky celkem 3:

  • categoryBitMask nám vlastně pouze určuje, jaký typ je daný objekt.
  • Dále nastavujeme contactTestBitMask, která slouží k tomu, abychom řekli SpriteKit o kolizích s jakou kategorií nám má dát vědět.
  • Třetí maska, collisionBitMask, určuje, od jaké kategorie se má automaticky objekt odrazit, jako by došlo ke kolizi ve skutečném světě. My ji všude nastavíme na 0, aby do sebe věci nenarážely a různě se nám nehýbaly mimo naši kontrolu.

Pokud vám v budoucnu nebudou v nějaké hře fungovat kolize, měly by bitové masky být první věc, kterou budete kontrolovat a případně zkoušet upravit.

Nastavení bitových masek objektům

Začneme ve třídě Player a konstruktoru init():

physicsBody?.categoryBitMask = ObjectType.player.rawValue
physicsBody?.contactTestBitMask = ObjectType.laser.rawValue
physicsBody?.collisionBitMask = 0

Dále musíme tyto masky nastavit pro rakety:

missile1.physicsBody?.categoryBitMask = ObjectType.missile.rawValue
missile1.physicsBody?.contactTestBitMask = ObjectType.enemy.rawValue
missile1.physicsBody?.collisionBitMask = 0
missile2.physicsBody?.categoryBitMask = ObjectType.missile.rawValue
missile2.physicsBody?.contactTestBitMask = ObjectType.enemy.rawValue
missile2.physicsBody?.collisionBitMask = 0

Může vás lákat využít metodu copy() a prostě nastavené physicsBody první rakety zkopírovat do druhé. To ale nebude fungovat, protože metody copy() v tomto případě vytvoří novou instanci SKPhysicsBody a nedojde k přenesení nastavených vlastností.

Zbývá nastavení pro nepřátele:

enemy.physicsBody?.categoryBitMask = ObjectType.enemy.rawValue
enemy.physicsBody?.contactTestBitMask = ObjectType.missile.rawValue
enemy.physicsBody?.collisionBitMask = 0

A laser:

laser.physicsBody?.categoryBitMask = ObjectType.laser.rawValue
laser.physicsBody?.contactTestBitMask = ObjectType.player.rawValue
laser.physicsBody?.collisionBitMask = 0

Získání informací o kolizích

Když teď hru zapnete, mělo by vše fungovat jako bychom nic nového nepřidali. Zbývá již ovšem jen minimum, abychom získávali informace o kolizích.

V první řadě musíme naší GameScene přidat protokol SKPhysicsContactDelegate a v didMove() ho nastavit:

physicsWorld.contactDelegate = self

O kolizích se dozvíme skrze metodu:

func didBegin(_ contact: SKPhysicsContact) {
        print("Contact!")
}

Pro vyzkoušení jsem doplnil print(). Když teď hru zapneme, můžeme sledovat výpis Contact! pokud raketa strefí nepřítele nebo laser loď hráče.

V příští lekci, Dokončení kolizí ve SpriteKit, již můžeme přidat exploze po kontaktu a také odstranit zničené nepřátele a rozhodnout se, jak moc bude hráč penalizovaný za zásah laserem.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 9x (954.48 kB)
Aplikace je včetně zdrojových kódů v jazyce Swift

 

Předchozí článek
Nepřátelé střílí zpět a dokonce laserem ve SpriteKit
Všechny články v sekci
SpriteKit - Tvorba iOS her ve Swift
Přeskočit článek
(nedoporučujeme)
Dokončení kolizí ve SpriteKit
Článek pro vás napsal Filip Němeček
Avatar
Uživatelské hodnocení:
1 hlasů
Autor se věnuje vývoji iOS aplikací (občas macOS)
Aktivity