Jak naprogramovat shmup pro Apple Watch
Návod na shmup pro Apple Watch: virtuální rozlišení, ovládání korunkou, kolize obdélníky, pooling, levely jako tabulka časů a generátor vln.

Shmup (shoot 'em up, střílečka, ve které loď letí proti vlnám nepřátel) na Apple Watch naráží na tři věci: malý displej, prst, který zakryje polovinu hry, a různé velikosti hodinek. Řeší se třemi rozhodnutími: celá hra běží v pevné mřížce 84 × 48 bodů, kolize se počítají ručně obdélníky a levely jsou tabulky s časy, ne kód. Loď se ovládá korunkou Digital Crown a střílí sama.
Postup ukazujeme na hře Space Impact Watch, remaku střílečky Space Impact z telefonu Nokia 3310 z roku 2000, která běží na hodinkách i na iPhonu a iPadu. Příklady jsou ze SpriteKitu, 2D herního frameworku od Applu, ale principy platí pro každý shmup na malou obrazovku.
Pevné virtuální rozlišení 84 × 48
Nejprve se rozhodněte, v jakých jednotkách bude hra počítat. Pokud použijete body obrazovky, svět bude na každé velikosti hodinek jinak velký. Lepší je zvolit malé virtuální rozlišení a všechno počítat v něm. V naší hře je to 84 × 48, přesně jako měl displej Nokie 3310. Pozice, rychlosti, velikosti spritů i dráhy nepřátel jsou v těchto jednotkách. Při startu scény se jednou spočítá, kolik bodů obrazovky má jeden virtuální pixel (šířka scény děleno 84), a vše se jím jen vynásobí.

Co tím získáte:
- Stejný vzhled na každém zařízení. Hra vypadá stejně na každé velikosti hodinek i na iPadu, mění se jen velikost bloku.
- Level design v malých celých číslech. Nepřítel letí ve výšce 13, raketa zatáčí v bodě x = 20.
- Jednoduchou grafiku. Sprity jsou jednobarevné a kreslené v desetinásobné velikosti, jeden virtuální pixel je ve zdrojovém obrázku blok 10 × 10. Barvu dostanou až ve hře. Na světlých levelech jsou černé na zeleném pozadí, na tmavých je to obráceně.
Ovládání korunkou a automatická střelba
Na hodinkách prst zakryje polovinu hry, proto se loď neovládá dotykem. Pohyb řeší Digital Crown: otočení korunky posune loď nahoru nebo dolů. Přidejte nastavení citlivosti a možnost otočit směr.
Střelba je automatická, v naší hře každých 0,4 sekundy, od čtvrtého levelu každých 0,3. Displej slouží jen pro jedno gesto: přetažením doprava hráč použije jednu ze tří speciálních zbraní.

Na hodinkách je vibrace při zásahu stejně důležitá jako animace, malý výbuch na displeji si hráč snadno nevšimne. Haptiku volejte přes jedno rozhraní (zásah, smrt nepřítele, poražený boss), které každá platforma provede po svém: hodinky vibrací, iPhone vlastním způsobem. Verze pro telefon nemá korunku, místo ní má tlačítka na displeji, která používají stejné nastavení citlivosti i směru.

Herní smyčka v jedné scéně
Celá hra může být jedna scéna s jednou smyčkou:
každý snímek:
posuň všechny objekty
zkontroluj kolize
podle času v levelu přidej nové nepřátele
Věci přidávejte v pořadí, ve kterém se dají co nejdříve testovat:
- Střelba a pohyb hráče. Bez nich se nedá nic otestovat.
- Nepřátelé a kolize. Prvnímu nepříteli stačí letět rovně doleva a zemřít po jednom zásahu.
- Vibrace a animace zásahu. Na malém displeji bez nich hráč neví, že zasáhl.
- Pozadí a HUD. Životy, skóre, počet speciálních zbraní. Pokud HUD v některém levelu překáží, přesuňte ho dolů.
Na konci máte hratelnou smyčku se životy, skóre, výbuchy, pozadím a respawnem hráče. Pokud herní logiku držíte odděleně od vykreslování a vstupu, mohou ji sdílet verze pro hodinky i telefon.
Kolize obdélníky místo fyzikálního enginu
SpriteKit má fyzikální engine a nabízí se použít ho i na kolize. Každý objekt dostane fyzikální tělo vytvořené z obrysu spritu, přesnou detekci kolizí a kategorii (hráč, nepřítel, střela, speciální zbraň) a engine sám hlásí dvojice, které se dotkly. Apple v dokumentaci k fyzikálním tělům uvádí, že těla z alfa kanálu textury jsou nejpřesnější a zároveň výpočetně nejdražší. V naší hře jsme to zkusili a vrátili jsme se k ruční kontrole.
Ruční kontrola je jedna funkce, která postupně projde dvojice skupin a porovná obdélníky:
pro každou dvojici skupin:
(hráč a nepřátelé, hráč a střely, střely a nepřátelé, střely a střely)
pro každý objekt A z první skupiny a objekt B z druhé:
pokud se obdélník A překrývá s obdélníkem B:
vyřeš zásah

Výhody: pořadí kontrol máte pod kontrolou a celé pravidlo „co se stane při zásahu“ je na jednom místě. Nepřesnost je malá, protože na mřížce 84 × 48 jsou sprity tak drobné, že obdélník a skutečný obrys se liší jen o pár virtuálních pixelů.
Pooling jen tam, kde se vyplatí
Pooling znamená, že objekt po zániku nesmažete, jen ho skryjete a příště použijete znovu. Je lákavé poolovat všechno včetně výbuchů a power-upů. Má to ale cenu: každá kolize a každý update musí nejdřív ověřit, jestli je objekt vůbec živý. Stačí jedna zapomenutá kontrola a skrytý nepřítel zabije hráče nebo skrytá střela sestřelí raketu. Když jsme v naší hře pooling ze všeho odstraňovali, mazali jsme hlavně tyto kontroly.
Jednoduché pravidlo: poolujte to, čeho je hodně a vzniká to pořád stejně. Hráč si při startu vytvoří 21 střel a pak je jen recykluje. Nepřátelé a jejich střely se také znovu používají, výbuchy a power-upy vznikají a zanikají normálně.
Levely jako tabulka časů
Level nemusí být kód, stačí seznam záznamů. Každý záznam říká, co se objeví, v které sekundě, v jaké výšce a jak se pohybuje. Pohyb je seznam bodů, kterými nepřítel letí stejnou rychlostí. Volitelně přibudou časy výstřelů, výpad bosse nebo generátor menších nepřátel.
každý snímek:
čas = teď - začátek levelu - čas v pauze
pro každý záznam v tabulce levelu:
pokud záznam ještě nebyl použit a čas > záznam.čas:
vytvoř objekt podle záznamu
označ záznam jako použitý

Takto zapsaný level se snadno ladí: posunout vlnu o půl sekundy znamená změnit jedno číslo. Na časové ose je vidět i rytmus, o který jde: krátká vlna, pauza, jiný typ nepřítele, power-up před těžším úsekem.

Nevýhodu cítit při větších změnách. Nejhustší level naší hry má 71 řádků a taková tabulka se bez editoru čte špatně. Výhodnější je zapisovat levely jako vlny (skupina nepřátel s jedním vzorem pohybu), ne jako jednotlivé lodě.
Nekonečný režim z jednoho pseudonáhodného čísla
Pro žebříček (v naší hře Game Center) potřebujete režim, který nekončí. Ručně psanou tabulku v něm nahradí generátor, který ji vyrábí za běhu. Vyrobí 20ciferné pseudonáhodné číslo a jednotlivé dvojice číslic rozhodnou o jedné vlně:
číslo = další pseudonáhodné 20ciferné číslo
typ = power-up, pokud číslice 4 až 5 > 90, jinak nepřítel
sprite = číslice 1 až 2 modulo počet druhů nepřátel
pauza před vlnou = číslice 3 / 2 sekundy (aspoň 1)
výška = číslice 7, přepočtená na výšku plochy
vzor pohybu = číslice 10 až 11 modulo 10
počet lodí ve vlně = číslice 17 / 2 + 1
rychlost = číslice 13 × (1 + pořadí vlny / 10), v rozsahu 20 až 50
každá třicátá vlna = boss, generování stojí, dokud boss žije
Seed generátoru (počáteční hodnotu) nechte ve hře pevný. Každý hráč pak dostane stejné pořadí vln a skóre v žebříčku se dá porovnávat.

Jak se takový režim vyvíjí, ukazuje simulace generátoru mimo hru (300 vln, stejný seed jako ve hře). První boss přijde po 29 vlnách, tedy po 74 nepřátelích a 3 power-upech, zhruba po 130 sekundách. Prvních 30 vln letí minimální rychlostí 20, protože jedna číslice je ještě menší než dolní hranice. Průměrná rychlost pak roste na 28,5, 37,5 a 41, od 120. vlny je většina nepřátel na maximu. Power-up je přibližně každá jedenáctá vlna.

Slabina je, že generátor nezná rytmus. Ruční level střídá tlak a oddech, generátor ne. Částečně to řeší pauza během bosse a růst rychlosti, ale ručně psané levely se hrají lépe.
Limity tohoto přístupu
- Žádné automatické testy. Všechno se ověřuje ručně v simulátoru a na hodinkách.
- Globální stav. Většina stavu hry jsou globální proměnné jedné scény. Pro hru jen na hodinky je to pohodlné, při přidání verze pro iPhone už ne.
- Levely bez editoru. Tabulka časů se dobře ladí po malých krocích, při větších změnách by si zasloužila editor.
Pokud děláte hru na hodinky nebo jinou malou obrazovku a potřebujete s něčím pomoct, napište nám. Jak generujeme hlavolamy v jiné naší aplikaci, jsme popsali v článku o generátoru sudoku.


