Slevový týden - Květen Slevový týden - Květen
30 % bodů zdarma na online výuku díky naší Slevové akci!

Lekce 11 - Nekonečné vlny nepřátel a jejich animace ve SpriteKit

V předchozí lekci, Poškození hráče, menu hry a restart ve SpriteKit, jsme si ukázali mimo jiné jak implementovat menu hry a přidali restart, pokud hráče nepřátelé zničí. Hráč ale pořád může dříve zničit vlnu nepřátel a potom již nerušeně pokračovat ve hře, protože se dále nic nestane.

K dokončení hry nám zbývá upravit, jak fungují vlny nepřátel, aby se po zničení objevila nová. Připravíme si novou metodu createEnemies(), která se bude starat o vytvoření všech nepřátel a také animaci jejich příletu na scénu.

Náhodné formace nepřátel

Aktuálně používáme metodu createEnemyWave(), která vytvoří vlnu nepřátel podle zadaného počtu. My bychom ovšem potřebovali, aby se vlny od sebe nelišily jen počtem nepřátel, ale také formací, v které letí.

createEnemyWave()

Nejprve upravíme createEnemyWave(), aby brala v potaz řady nepřátel. Přidáme tedy parametr row typu Int:

func createEnemyWave(enemyCount: Int, row: Int)

Po úpravě také smažeme volání metody v didMove(), ať na nás Xcode nekřičí, že nesedí parametry.

A uvnitř metody nad cyklem vypočítáme pozici y:

let yPosition: CGFloat = CGFloat(row) * 120

Tu potom nastavíme nepřátelům v těle cyklu:

newEnemy.position = CGPoint(x: xPosition, y: yPosition)

Na závěr přidáme náhodný výběr typu nepřátel upravením volání Enemy.create() na začátku metody:

let enemyTemplate = Enemy.create(variant: Int.random(in: 1...3))

createEnemies()

A teď se můžeme pustit do metody createEnemies():

func createEnemies() {
    let rows = Int.random(in: 2...3)
    for row in 1...rows {
        createEnemyWave(enemyCount: Int.random(in: 3...5), row: row)
    }
}

Odměnou za docela složitou metodu createEnemyWave() je její jednoduché použití, když chceme podle náhody připravit dvě nebo tři řady nepřátel a v každé mít v rozmezí od 3 do 5 nepřátelských lodí.

setupEnemyAnchor()

Ještě musíme upravit metodu setupEnemyAnchor(), konkrétně pozici, aby bylo místo na tři řady nepřátel:

enemyAnchor.position = CGPoint(x: 0, y: size.height - 550)

Můžeme vyzkoušet:

Každé zavolání createEnemies() vytvoří náhodnou nepřátelskou formaci :-)

Generování nových nepřátel

Teď stačí přidat animaci příletu a generovat nové nepřátele, když hráč všechny zlikviduje.

isGameInProgress

Musíme hlavně vyřešit, aby nepřátelé nestříleli, pokud zrovna animujeme jejich přílet. Mohli bychom si vytvořit další bool proměnnou. Přehlednější a jednodušší bude ale upravit současnou proměnnou isGameOver, kterou přejmenujeme na obecnější isGameInProgress.

Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

Ze začátku bude nastavena rovněž na false a vždy ji nastavíme na true, když nepřátelé přilétnou. Na false ji opět nastavíme v případě, když hráč ztratí všechny životy nebo zlikviduje všechny nepřátele. Začneme tedy s přejmenováním:

var isGameInProgress = false

lives

Upravíme didSet blok proměnné lives:

if lives < 0 {
    isGameInProgress = false
    controller?.showGameOver()
    return
}

didBegin()

Další úpravu je třeba provést na začátku metody didBegin():

guard isGameInProgress else { return }

enemyFireTimerTick()

A konečně v metodě enemyFireTimerTick(), kde na začátek přidáme to samé:

guard isGameInProgress else { return }

To samé bychom mohli udělat v metodě playerFireTimerTick(), ale díky kontrole v didBegin() jsou stejně rakety hráče neškodné. Opět je to na vás.

enemyCount

Abychom mohli zjistit, jestli už byli zničeni všichni nepřátelé, přidáme si vlastnost enemyCount, která se jednoduše dotáže na potomky enemyAnchor:

var enemyCount: Int {
    return enemyAnchor.children.count
}

missileHit()

Zbývá přidat kontrolu na konec metody missileHit(). Zde ale pozor. Nemůžeme se na konci jednoduše zeptat na enemyCount, protože nepřátele ničíme nejprve fadeOut akcí, která nějaký čas trvá. Na enemyCount bychom se tedy zeptali dříve, než dojde ke zničení nepřítele.

Zjistil jsem, že na tomto řádku:

let fadeOut = SKAction.fadeOut(withDuration: 0.2)

Jsem měl překlep a použil jsem akci SKAction.fadeIn() s opačným efektem, tak si to prosím opravte a omlouvám se za komplikace :-)

Zpět k našemu problému. Vytvoříme si obecnou SKAction, která zkontroluje počet nepřátel:

let createNewEnemiesIfNeeded = SKAction.run {
    if self.enemyCount == 0 {
        self.isGameInProgress = false
        self.createEnemies()
    }
}

A pak ji jen přidáme na konec existující sekvence:

let sequence = SKAction.sequence([fadeOut, SKAction.removeFromParent(), createNewEnemiesIfNeeded])

A máme nekonečné nepřátele. Zbývá jejich animace.

Animace vlny nepřátel

Upravíme metodu createEnemies(), aby se nepřátelé prostě neobjevili, ale místo toho přilétli. Na její začátek tedy přidáme posunutí enemyAnchor mimo viditelnou scénu a odstranění všech akcí, protože pro nepřátele konfigurujeme nekonečný pohyb do stran. Takto by nám akorát rozhazoval naše chystané animace.

enemyAnchor.position = CGPoint(x: 0, y: size.height + 700)
enemyAnchor.removeAllActions()

Ještě nezapomeňte z metody didMove() odstranit volání startEnemyMovement(). Stejně tak můžeme pročistit setupEnemyAnchor() a odstranit nastavení pozice:

enemyAnchor.position = CGPoint(x: 0, y: size.height - 550) // není již třeba

Posunutí pozice enemyAnchor výše by nám mělo poskytnout dost prostoru na vytvoření nepřátel, aniž by byli vidět.

Pod existujícím for cyklem si připravíme animace a další 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 hodně, ale nejedná se o nic komplikovaného :-) Nejdříve zmenšíme vytvořené nepřátele a potom je zároveň pomocí SKAction.group posuneme do viditelné scény a zvětšíme na původní velikost.

Potom již jen přes SKAction.run spustíme hru, včetně pohybu nepřátel.

Můžeme vyzkoušet:

A tím je naše vesmírná střílečka Galaxy Invaders ve SpriteKit hotová. Děkuji za zájem! :)

V tutoriálu jsem se snažil ukázat co možná nejvíce technik a možností, které nám SpriteKit ke tvorbě 2D her pro iOS poskytuje. Ukázali jsme si základní práci s texturami a herními objekty, docela dost se věnovali částicovým efektům, zapracovali jsme kolize pomocí fyziky a mnoho dalšího.

Možná vylepšení

Do hry je toho možné samozřejmě ještě kupu přidat. A jestli se hrám plánujete věnovat, rozhodně bych doporučil zkusit si Galaxy Invaders co nejvíce rozšířit. Přeci jen experimentováním a úpravami se můžete mnoho naučit.

Například můžete přidat další zvukové efekty nebo třeba hudbu na pozadí pomocí SKAudioNode, kterou stačí jen vytvořit a přidat do scény. O přehrávání audia se postará sama a funguje v nekonečné smyčce.

Vlny nepřátel aktuálně generujeme, lepší by bylo nadefinovat je a nabídnout hráči, aby postupoval v úrovních. Podle dané úrovně můžete třeba i měnit, jak často nepřátelé střílí. Stejně tak by nebylo těžké přidat více typů pohybů nepřátel a třeba mezi nimi náhodně vybírat nebo zkusit nabídnout hráči souboj s jedním velkým nepřítelem, kterého nezničí jeden zásah, takže u něj bude třeba ukládat poškození a třeba i změnit jeho bojový arzenál.

Dále můžete přidat třeba power-upy, které hráč pomocí kolize sebere. Například rychlejší frekvenci střelby, doplnění životů nebo sebrání štítu, který balíček od Kenney.nl obsahuje.

A určitě sami vymyslíte spoustu dalších možností, jak hru vylepšit.

Nezapomeňte, že své výsledné výtvory můžete nahrát na ITnetwork a třeba ostatní inspirovat nebo jim ukázat, jak další rozšíření implementovat.

Pokud si nebudete vědět s něčím rady, určitě se nebojte napsat do komentářů, na zdejší fórum (klidně mě můžete v příspěvku označit, ať o něm vím) nebo do soukromých zpráv. Rád pomohu, pokud to bude v mých silách.


 

Stáhnout

Staženo 4x (1.73 MB)
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 - mrkněte na veřejné projekty
Předchozí článek
Poškození hráče, menu hry a restart ve SpriteKit
Všechny články v sekci
Tvorba iOS her ve Swift
Aktivity (3)

 

 

Komentáře

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.

Zatím nikdo nevložil komentář - buď první!