Další šance dokončit svůj projekt a získat ceny v hodnotě 10.000 Kč! Pokračování úspěšné letní soutěže - ITnetwork winter

Lekce 2 - Pozadí, ovládání hráče a částicové efekty ve SpriteKit

Unicorn College Tento obsah je dostupný zdarma v rámci projektu IT lidem.
Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V předchozí lekci, Úvod do tvorby iOS her s frameworkem SpriteKit, jsme si představili SpriteKit včetně struktury projektu a nachystali textury pro vesmírnou střílečku. Nyní se pustíme do budování naší hry.

Nakopírování textur

Jako první krok je třeba nakopírovat naše obrázky do Assets.xcassets v projektu a ideálně zvolit nějaké výstižné a jednoduché názvy. My si zvolíme background pro pozadí a player pro loď hráče.

Pozicování ve SpriteKit

Začneme s vytvořením pozadí. Než si pozadí přidáme do naší scény, potřebujeme si vysvětlit, jak funguje souřadnicový systém SpriteKit. Ve výchozím stavu totiž nezačíná v levém horním rohu jako UIKit.

Když si otevřete GameScene.sks, tak napravo v Attributes inspektoru můžete vidět informace o naší scéně. Nás zajímá položka "Anchor". Je to vlastně taková virtuální kotva ve scéně, podle které se budou umisťovat další prvky.

Ve výchozím stavu je nastavena na X: 0.5 a Y: 0.5. To znamená střed scény. V praxi se používá buď toto nastavení nebo X: 0.0 a Y: 0.0, což znamená levý dolní roh.

Rozhodnutí jaký Anchor zvolit je dost podstatné a nejde udělat jednoznačné doporučení. Záleží totiž na tom, jak je vaše hra koncipovaná. Pokud bychom tvořili hru, ve které bude obrazovka celá herní aréna a hráč se nebude pohybovat mimo, tak je nejlogičtější ponechat právě 0.5;0.5 a vše nastavovat vzhledem ke středu. Naše hra bude mít ve spodní části loď hráče, iluzi letu vesmírem a nepřátele v horní části obrazovky. Z pohledu této hry dává smysl použít Anchor 0.0;0,0.

Budu tedy používat toto pozicování. Pokud se vám lépe uvažuje a pracuje s jiným, tak ho samozřejmě používejte. Jen budete muset při každém pozicování přijít na souřadnice pro váš Anchor, aby byl výsledek stejný.

Protože je pozicování důležité, připravil jsem ještě ilustrační obrázek různého nastavení Anchor:

Anchors ve SpriteKit

Pozadí

Přesuneme se do GameScene.swift. Jak již víme z první lekce, máme zde k dispozici metodu didMove(), která je zavolána vždy na začátku a můžeme v ní nastavit všechno ve scéně.

Podobně jako s viewDidLoad() doporučuji vytvářet pro nastavení jednotlivých věcí ve scéně oddělené metody a ty poté volat v didMove(), aby se její tělo četlo jako seznam příkazů. Díky tomu mnohem snadněji zjistíte, co má vlastně metoda na starost. Vytvoříme si tedy novou metodu createBackground(), ve které pozadí vytvoříme. Vlastně skoro jakýkoliv objekt ve SpriteKit má za potomka třídu SKNode, která slouží jako takový základní stavební blok. My budeme často používat SKSpriteNode, jenž je specializovaná na zobrazování 2D objektů.

Začneme tedy jejím vytvořením a použijeme náš obrázek jako texturu:

let background = SKSpriteNode(imageNamed: "background")

Nyní nastavíme vlastnost zPosition, což je klasická z souřadnice určující, v jakém pořadí jsou na sobě objekty "naskládané". Pozadí bude logicky nejníže, takže mu dáme např. -5.

background.zPosition = -5

Zbývá nastavit pozici a přidat objekt do scény. Pozici musíme nastavit z důvodu, že jsme změnili výchozí Anchor ze středové (0.5;0.5) na levý dolní roh, ale pozice jakýchkoliv dalších SKNode je stále výchozí, tedy (0.5;0.5). Pozici tedy posuneme o polovinu šířky a o polovinu výšky scény a pomocí addChild() přidáme pozadí jako objekt scény:

background.position = CGPoint(x: size.width / 2, y: size.height / 2)
addChild(background)

Nyní stačí createBackground() zavolat v didMove() a hru již můžete zapnout. Uvidíte pozadí.

Hráč

Máme pozadí a podobným stylem přidáme do hry vesmírnou loď hráče. Opět si tedy vytvoříme metodu setupPlayer(), ovšem samotný objekt inicializujeme na úrovni třídy, abychom k němu mohli přistupovat:

let player = SKSpriteNode(imageNamed: "player")
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

A nyní již v těle metody zatím přidáme tyto řádky, které hráče přidají dolů doprostřed:

player.position = CGPoint(x: size.width / 2, y: 120)
addChild(player)

Ovládání

S lodí hráče budeme horizontálně hýbat pomocí tažení prstu. To je intuitivní a na implementaci jednoduchá metoda. Začneme v metodě touchesMoved(), kterou jsme sice při našem čistění smazali, ale není problém ji pomocí nápovědy dostat zpět:

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
}

Metoda je zavolána vždy, když hráč pohne prstem po displeji a dostaneme sadu dotyků. Zde je nejjednodušší vzít ten první a zjistit jeho umístění ve scéně:

guard let first = touches.first else { return }
let touchPosition = first.location(in: self)

Potom již stačí jen upravit pozici hráče takto:

player.position.x = touchPosition.x

Protože nesaháme na souřadnici y, tak se bude hráč pohybovat jen horizontálně. Můžete hru zapnout a vyzkoušet.

Vyjetí z obrazovky a teleport

Jsou tu dva menší problémy. Hráč může loď dostat zhruba z poloviny mimo obrazovku, což není zrovna hezké. Kromě toho může využít teleportu, když se prstem nedotkne přímo lodi.

Vyjetí z obrazovky

První problém vyřeší guard před nastavením nové pozice hráče, prostě se zeptáme, jestli je nová souřadnice x větší než šířka lodi a zároveň menší než šířka scény - šířka lodi:

guard touchPosition.x > player.size.width && touchPosition.x < size.width - player.size.width else { return }

Opět můžete vyzkoušet :-)

Teleport

A nyní zamezení teleportu. Nejjednodušší bude vytvoření bool proměnné, které nám řekne, jestli se hráč dotkl lodi. Jinak mu pohyb nedovolíme. Vytvoříme tedy proměnnou:

var shouldMovePlayer = false

A přesuneme se do metody touchesBegan(). Zde se opět zeptáme na první dotek a pomocí dostupných metod zjistíme, jestli se hráč dotkl lodi. Celé to bude vypadat takto:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let first = touches.first else { return }
        let tapped = nodes(at: first.location(in: self))
        shouldMovePlayer = tapped.contains(player)
}

Metoda nodes(at: ) je velmi užitečná. Ze zadané pozice ve scéně nám vrátí všechny objekty (tedy SKNode), které se na daném místě nacházejí.

Potom už stačí upravit touchesMoved() a přidat na začátek další guard, kde ověříme přidanou bool proměnnou:

guard shouldMovePlayer else { return }

A samozřejmě nesmíme zapomenout na situaci, kdy hráč přestane jezdit prstem po displeji. V metodě touchesEnded() nastavíme proměnnou opět na false:

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        shouldMovePlayer = false
}

Můžete vyzkoušet. Teď už jde posouvat loď jen při přímém dotyku.

Částicové efekty

Na závěr této lekce si ukážeme, jak hru oživit částicemi. Ty nám později poslouží k vytvoření explozí. Zatím je využijeme na simulování "prachu" ve vesmíru, abychom získali dojem, že se hráč skutečně pohybuje.

Částicové efekty mohou znít jako obtížná část vývoje her, ale SpriteKit nám práci s nimi podstatně ulehčuje. Primárně díky vizuálnímu editoru a připraveným šablonám.

Hvězdný prach

Přidejte si tedy do projektu nový soubor a jako typ vyberte "SpriteKit Particle File" a jako šablonu "Snow". Jako název jsem zvolil Space Dust.

Na začátek vypadá výchozí sníh docela dobře. Padá směrem, který chceme, takže nás čekají spíše menší úpravy.

Nejdříve upravíme hodnotu "Position range" na 750 pro x (což je šířka scény) a 0 pro y. To znamená, že se částice budou objevovat se souřadnicí x někde v rozmezí 0750.

Dále jsem nastavil "Speed" na hodnoty 20 pro "Start" a 0 pro "Range". Tedy konstantní rychlost. Částice jsem rovněž zmenšil nastavením "Scale" na 0.05 a "Range" na stejnou hodnotu.

Počet se ovládá pomocí nastavení "Emitter" úplně nahoře, kde jsem pro "Birthrate" nastavil 20. Jako poslední jsem nastavil "Lifetime" na 20, aby částice nezmizely, dokud jsou vidět na obrazovce.

Zde samozřejmě zas platí, že hodnoty výše jsou spíše doporučené. Zkuste si s nastavením pohrát a vytvořit si částice vlastní :-)

Přidání částic do scény

Nyní již stačí přidat částice do scény. Opět začneme s metodou createParticles(). Pro částice máme speciální SKEmitterNode, kterou vytvoříme a předáme ji náš soubor:

func createParticles() {
        if let spaceDust = SKEmitterNode(fileNamed: "SpaceDust") {
            spaceDust.zPosition = -1
            spaceDust.position = CGPoint(x: size.width / 2, y: size.height)
            addChild(spaceDust)
        }
}

Je to podobné, jako u pozadí a hráče. Vytvoříme objekt, nastavíme zPosition a position tak, aby částice začínaly uprostřed horní hrany. Potom už jen stačí přidat do scény.

Nyní můžete hru vyzkoušet, bude o poznání živější.

advanceSimula­tionTime()

Máme tu ale problém, protože po spuštění efekt nevypadá dobře, jelikož částicím zabere, než zaplní celou obrazovku.

Naštěstí má SKEmitterNode skvělou metodu, která tohle řeší. Jmenuje se advanceSimulationTime() a dovolí nám vlastně "přetočit" čas simulace. Nastavil jsem hodnotu na 15, těsně před addChild(spaceDust) a po spuštění výsledek vypadá mnohem lépe.

Výsledek je velmi působivý:

Částicový efekt v SpriteKit pro iOS

Pokračovat budeme příště, v lekci Nepřátelé a jejich pohyb ve SpriteKit.


 

Stáhnout

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

 

 

Článek pro vás napsal Filip Němeček
Avatar
Jak se ti líbí článek?
1 hlasů
Autor se věnuje vývoji iOS aplikací (občas macOS) či těch webových ve frameworku Django. Twitter: @nemecek_f | GitHub nemecek-filip
Předchozí článek
Úvod do tvorby iOS her s frameworkem SpriteKit
Všechny články v sekci
Tvorba iOS her ve Swift
Miniatura
Následující článek
Nepřátelé a jejich pohyb ve SpriteKit
Aktivity (4)

 

 

Komentáře

Avatar
Radek
Člen
Avatar
Radek:13. prosince 22:59

Zatím funguje i na AppleTV, jen na reálném zařízení očekávaně nefungují metody pro dotykové ovládání - potrápím stackoverflow a dokumentaci a předělám na ovládání nakláněním originálního ovladače a gamepadem. Ale zase na druhou stranu už teď vím, že díky tomuto stylu ovládání tam bude moci být player1 a player2 :-D

 
Odpovědět
13. prosince 22:59
Avatar
Filip Němeček
Redaktor
Avatar
Odpovídá na Radek
Filip Němeček:14. prosince 9:56

Tyjo cool! :-) Já jsem zatím zkoušel pro tvOS drobnosti, je fajn, že ten systém vychází z iOS, takže se mu hodně podobá. Tutoriál tady je pro iPhone čistě proto, aby byl dostupný co nejvíce lidem.. Ovládání nakláněním můžeš řešit pomocí manipulace s gravitací herního světa, ale k tomu je potřeba fyzika :-)

 
Odpovědět
14. prosince 9:56
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
Radek
Člen
Avatar
Odpovídá na Filip Němeček
Radek:14. prosince 11:04

Nakonec jsem se nejprve vrhnul na game controller (Nimbus Steelseries). Kompletní a rozumný návod je zde: https://medium.com/…a9b8308ce0b4
Jen je problém, že neřeší držení stisknutého ovladače. To jsem nakonec dal také, kdy v té extension nastavuji "globální" boolean jestli uživatel drží ovladač stisknutý, v update() se pak provádí kontrola jestli drží a pokud ano a není mimo plochu, tak se volá self.player.run(rig­htMoveAction) a self.player.run(lef­tMoveAction), kde jsou to metody založené na SKAction.move s poměrně krátkým duration.

Zdá se že to funguje rozumně.

Editováno 14. prosince 11:05
 
Odpovědět
14. prosince 11:04
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 3 zpráv z 3.