Creați primul dvs. joc iPhone de la zero, fără experiență de codare necesară.

Introducere

Acest tutorial a fost scris pentru Swift 4.0 / XCode 9, eventualele versiuni viitoare pot avea probleme până când actualizez articolul.

Deci vă interesează să construiți jocuri mobile? Poate este atragerea decolării și a cumpărării unei mașini noi sau poate este pur și simplu pasiunea de a-ți crea propriul joc. Oricare ar fi motivul dvs., acest tutorial vă va parcurge pașii de configurare a computerului pentru a construi aplicații și a crea un joc simplu de la zero! Tot codul jocului este inclus în tutorial; nu este nevoie de cunoștințe prealabile despre programare pentru a finaliza acest proiect.

După ce ai finalizat acest tutorial, vei putea rula o aplicație IOS de construcție proprie pe dispozitivul tău sau pe un simulator de dispozitiv. Veți înțelege elementele de bază ale proiectării unui joc de la zero, cum puteți salva date pe dispozitivul dvs. după închiderea aplicației, cum să faceți sprituri pe ecran și veți începe să înțelegeți cum să folosiți motorul de joc SpriteKit. De asemenea, vă voi ghida prin modul în care am proiectat jocul Snake și cum puteți începe să construiți un joc de design propriu!

Iată un link de descărcare către o versiune ușor modificată a acestui joc, disponibil în App Store: https://itunes.apple.com/us/app/minimal-snake/id1355406338?mt=8 (complet gratuit, fără reclame).

Notă: Pe parcursul articolului am folosit termenul copy / paste, un cititor mi-a atras atenția că aceasta este o practică proastă și sunt total de acord. Dacă doriți să copiați rapid codul și să creați un produs care funcționează bine, cu toate acestea, probabil că veți obține mai multe din notarea fiecărei linii de mână!

Produs completat

Iată un videoclip care arată ce veți fi construit și instalat pe telefonul dvs. până la sfârșitul acestui tutorial!

Noțiuni de bază

Pentru a urma acest tutorial, va trebui să configurați un cont pentru dezvoltatori Apple și să descărcați Xcode (programul folosit pentru a construi aplicații IOS). Din păcate, Xcode este disponibil numai pentru Mac-uri; dacă aveți o mașină Windows / Linux, aici este un site care vă poate ajuta să configurați Xcode.

Acești pași următori vor parcurge înregistrarea unui cont de dezvoltator gratuit și instalarea Xcode. Dacă aveți deja un cont și un cod X, puteți să treceți la secțiunea următoare. Pentru a începe mai întâi, vizitați developer.apple.com și faceți clic pe centrul de membri, apoi conectați-vă cu ID-ul Apple. Accesați pagina Acordului pentru dezvoltatori Apple și acceptați acordul; acum aveți un cont de dezvoltator gratuit! Pentru a vă încărca proiectele în magazinul de aplicații, va trebui să plătiți o taxă anuală de 100 $.

Acum că aveți un cont de dezvoltator, trebuie să instalați Xcode. Xcode este disponibil prin intermediul Mac App Store. După ce ați instalat Xcode, lansați programul și faceți clic pe Xcode -> Preferințe -> Conturi -> + și alegeți Adăugare Apple ID. Autentificați-vă cu ID-ul Apple care a fost înregistrat pentru contul de dezvoltator. Felicitări, puteți testa acum aplicațiile într-un simulator pentru iPhone sau le puteți rula pe dispozitivul personal!

Începerea proiectului

După ce v-ați înregistrat pentru un cont de dezvoltator și ați instalat Xcode, puteți începe să dezvoltați primul dvs. joc mobil!

Lansați Xcode și faceți clic pe „Creați un nou proiect Xcode”.

Faceți clic pe șablonul „Joc”.

Introduceți numele „Șarpe” (sau orice ați dori) pentru jocul dvs. Alegeți un nume de organizație, dacă aveți un site web, puteți să îl introduceți înapoi (com.gavinshrader) sau puteți pur și simplu să folosiți numele dvs. ca identificator. Asigurați-vă că limba este setată pe „Swift” și că tehnologia jocului este „SpriteKit”. Debifați cele 3 casete de selectare dacă sunt selectate.

Faceți clic dreapta pe „Actions.sks” și treceți la coșul de gunoi. Accesați GameScene.sks și faceți clic pe textul „Hello World”, apoi ștergeți-l. Accesați GameScene.swift și eliminați tot codul pre-construit, astfel încât fișierul să se potrivească cu imaginea de mai jos.

Creați un nou fișier Swift fie accesând File -> New File și făcând clic pe Swift File, fie făcând clic dreapta pe folderul proiectului („Snake”) și selectând noul fișier. Găsiți pictograma Swift File evidențiată mai jos, dacă nu este prezentă tipul „Swift” în bara de filtrare. Introduceți numele „GameManager” și asigurați-vă că proiectul dvs. („Snake”) este selectat sub ținte, faceți clic pe „Create” pentru a crea noul dvs. fișier rapid.

Construirea meniului jocului

Înainte de a începe codificarea, verificați dacă proiectul dvs. se compilează după modificările pe care le-ați făcut în ultima secțiune. Selectați un dispozitiv din lista simulatoarelor, faceți clic pe butonul unde se află „iPhone 6”, probabil va fi etichetat „dispozitiv iOS generic”. Dacă doriți să testați pe un dispozitiv fizic conectați-vă iPhone, acordați Xcode câteva momente, apoi faceți clic pe dispozitivul dvs. După ce ați făcut acest lucru, faceți clic pe butonul de rulare triunghiulară. Dacă ați ales un dispozitiv simulat, acest ecran ar trebui să apară:

Dacă pe ecran apare „Hello World”, asigurați-vă că ați șters eticheta accesând GameScene.sks, făcând clic pe etichetă și apoi selectând ștergeți.

Suntem în sfârșit gata să începem să construim jocul! Când începeți un joc, vă ajută să vă aranjați ecranele dinainte. În acest joc vom începe cu un ecran de meniu simplu care arată titlul / sigla jocului. Un buton de redare va lansa un ecran de joc cu o zonă de joc și două etichete pentru scorul curent și cel mai bun scor. Când mori ecranul jocului final va fi afișat cu o opțiune de a reda.

Pentru a ne derula jocul, trebuie mai întâi să construim un meniu pentru a începe jocul. Vom începe prin scrierea codului pentru inițializarea unui meniu adăugând titlul jocului, o etichetă „cel mai bun punctaj” și un buton de redare. Deschideți fișierul GameScene.swift și copiați tot codul de jos, astfel încât fișierul să se potrivească cu imaginea (Figura A).

// 1
var gameLogo: SKLabelNode!
var bestScore: SKLabelNode!
var playButton: SKShapeNode!
// 2
initializeMenu ()
// 3
private func initializeMenu () {
    // Creați titlul jocului
    gameLogo = SKLabelNode (fontNamed: "ArialRoundedMTBold")
    gameLogo.zPozition = 1
    gameLogo.position = CGPoint (x: 0, y: (frame.size.height / 2) - 200)
    gameLogo.fontSize = 60
    gameLogo.text = "SNAKE"
    gameLogo.fontColor = SKColor.red
    self.addChild (gameLogo)
    // Creați eticheta cu cel mai bun punctaj
    bestScore = SKLabelNode (fontNamed: "ArialRoundedMTBold")
    bestScore.zPosition = 1
    bestScore.position = CGPoint (x: 0, y: gameLogo.position.y - 50)
    bestScore.fontSize = 40
    bestScore.text = "Cel mai bun punctaj: 0"
    bestScore.fontColor = SKColor.white
    self.addChild (bestScore)
    // Creați butonul de redare
    playButton = SKShapeNode ()
    playButton.name = "play_button"
    playButton.zPosition = 1
    playButton.position = CGPoint (x: 0, y: (frame.size.height / -2) + 200)
    playButton.fillColor = SKColor.cyan
    let topCorner = CGPoint (x: -50, y: 50)
    let bottomCorner = CGPoint (x: -50, y: -50)
    let middle = CGPoint (x: 50, y: 0)
    let path = CGMutablePath ()
    path.addLine (către: topCorner)
    path.addLines (între: [topCorner, bottomCorner, middle])
    playButton.path = calea
    self.addChild (playButton)
}
Figura A

Compilați-vă codul și verificați dacă dispozitivul dvs. arată imaginea de sus. Iată o explicație pentru ceea ce se întâmplă aici, acesta poate părea un zid de cod, dar este ușor de înțeles atunci când îl descompun.

  • 1: Creăm variabile pentru logo-uri / butoane. „!” După numele variabilei înseamnă că trebuie să inițializăm variabilele, ele nu pot fi goale sau „nul”.
  • 2: Apelăm funcția „initializeMenu ()” odată ce încărcarea vizualizării jocului. didMove (spre: vizualizare: SKView) este funcția care se numește odată ce GameScene a fost încărcat.
  • 3: Aceasta este funcția intializeMenu () pe care am scris-o pentru a crea obiectele din meniu.
  • 4/5/6: Creați obiectele și adăugați-le la GameScene apelând „self.addChild ()”.
  • 7: Am ales să folosesc SKShapeNodes pentru acest proiect datorită simplității lor, aceasta este o alternativă la crearea graficii dvs. într-un editor de imagini. Această linie de cod creează o cale sub forma unui triunghi. Vă rugăm să rețineți dacă intenționați să construiți și să publicați o aplicație, ar trebui să utilizați SKSpriteNodes pentru a încărca o imagine pe care ați creat-o, ShapeNodes poate provoca probleme de performanță atunci când sunt utilizate în cantități mari, deoarece sunt desenate dinamic o dată pe cadru.
  • 8: Setați calea triunghiulară pe care am creat-o pe sprite playButton și adăugați-o în GameScene.

Jucand jocul

Acum că avem o configurație simplă de meniu, să funcționăm butonul de redare. Mai întâi mergeți la fișierul dvs. GameManager.swift și înlocuiți tot codul cu acesta pentru a se potrivi cu imaginea de mai jos (Figura B).

import SpriteKit
class GameManager {
}
Figura B

Copiați codul de mai jos în fișierul dvs. GameScene.swift, astfel încât să se potrivească cu imaginea de mai jos (Figura C).

// 1
joc var: GameManager!
// 2
game = GameManager ()
// 3
Anulați funcția atingeBegan (_ atinge: Setați , cu eveniment: UIEvent?) {
    pentru atingere în atingeri {
        let location = touch.location (in: self)
        lasa touchNode = self.nodes (la: locatie)
        pentru nodul în touchNode {
            if node.name == "play_button" {
                incepe jocul()
            }
        }
    }
}
// 4
private func startGame () {
    print („joc de început”)
}
Figura C
  • 1: Inițializează un obiect GameManager. Mai multe detalii mai târziu ... aceasta va conține date de scor și va gestiona mișcarea jucătorului.
  • 2: Setați variabila de joc la un nou obiect GameManager ().
  • 3: Această funcție este numită de motorul jocului de fiecare dată când un utilizator atinge ecranul. Reamintim că butonul de redare pe care l-am creat anterior are numele „play_button”. Folosind numele putem verifica dacă utilizatorul a atins un SpriteNode cu numele „play_button”, odată ce se întâmplă, apelăm la funcția startGame () din punctul 4.
  • 4: Această funcție începe jocul.

Asigurați-vă că codul dvs. funcționează corect rulând aplicația și făcând clic pe butonul de redare triunghiulară. Dacă atingerile sunt măsurate corespunzător, consola ar trebui să afișeze „jocul de pornire”, așa cum se observă în imaginea de mai jos (figura D).

Figura D

Dacă consola dvs. nu se afișează, mergeți la bara de sus și faceți clic pe „Ajutor”, în bara de căutare tip „Console”, apoi faceți clic pe „Zona de depanare> Activare consolă”. Avem acum un sistem de meniu de lucru și un buton de redare, de aici putem începe cu adevărat să ne distrăm.

Se încarcă vizualizarea jocului

Deci avem un buton de redare care poate declanșa o funcție, ce ar trebui să facem? Pentru a arăta vizualizarea jocului, trebuie mai întâi să ascundem butoanele meniului. Adăugați această linie de cod pentru a ascunde butoanele de meniu cu o animație simplă. Codul dvs. ar trebui să se potrivească cu imaginea de mai jos (Figura E).

//incepe jocul
private func startGame () {
    print („joc de început”)
    // 1
    gameLogo.run (SKAction.move (de: CGVector (dx: -50, dy: 600), durata: 0,5)) {
    self.gameLogo.isHidden = true
    }
    // 2
    playButton.run (SKAction.scale (până la: 0, durata: 0.3)) {
        self.playButton.isHidden = true
    }
    // 3
    let bottomCorner = CGPoint (x: 0, y: (frame.size.height / -2) + 20)
    bestScore.run (SKAction.move (până la: bottomCorner, durata: 0.4))
}
Figura EFigura F
  • 1: mutați joculLogo de pe ecran și apoi ascundeți-l de la vedere. Parantezele după SKAction se execută după finalizarea acțiunii. De exemplu, dacă rulăm o SKAction de durată 10, codul din paranteză ar rula după 10 secunde. Iată un exemplu:
exampleNode.run (SKAction.move (de: CGVector (dx: 0, dy: 0), durata: 10) {
    print („Am ajuns după 10 secunde”)
}
  • 2: Scalați butonul de redare la 0; această acțiune micșorează butonul și apoi îl ascunde din vedere.
  • 3: mutați eticheta bestScore în partea de jos a ecranului.

Meniul dvs. ar trebui să se comporte astfel ca acest gif (figura F) când faceți clic pe butonul de redare!

Acum vom începe să proiectăm partea „șarpe” propriu-zisă a acestui joc, începeți prin adăugarea acestor linii de cod, astfel încât codul dvs. să se potrivească cu imaginea de mai jos (figura G).

// 1
var actual Scor: SKLabelNode!
var playerPozitions: [(Int, Int)] = []
var gameBG: SKShapeNode!
var gameArray: [(nod: SKShapeNode, x: Int, y: Int)] = []
// 2
initializeGameView ()
// 3
private func initializeGameView () {
    // 4
    currentScore = SKLabelNode (fontNamed: "ArialRoundedMTBold")
    currentScore.zPosition = 1
    currentScore.position = CGPoint (x: 0, y: (frame.size.height / -2) + 60)
    currentScore.fontSize = 40
    currentScore.isHidden = true
    currentScore.text = "Scor: 0"
    currentScore.fontColor = SKColor.white
    self.addChild (currentScore)
    // 5
    let width = frame.size.width - 200
    let înălțime = frame.size.height - 300
    let rect = CGRect (x: -wthth / 2, y: -height / 2, lățime: lățime, înălțime: înălțime)
    gameBG = SKShapeNode (rect: rect, cornerRadius: 0.02)
    gameBG.fillColor = SKColor.darkGray
    gameBG.zPozition = 2
    gameBG.isHidden = true
    self.addChild (gameBG)
    // 6
    createGameBoard (lățime: lățime, înălțime: înălțime)
}
Figura GFigura G
  • 1: Noi variabile! Creăm o etichetă pentru a arăta scorul curent, un tablou cu toate pozițiile pe care „șarpele” sau jucătorul le are în prezent, un fundal pentru vizualizarea jocului nostru și un tablou pentru a urmări pozițiile fiecărei celule din vizualizarea jocului.
  • 2: Apelați funcția initializeGameView ().
  • 3: inițializează vizualizarea jocului.
  • 4: Adăugați eticheta punctajului curent pe ecran, aceasta este ascunsă până când părăsim meniul nostru.
  • 5: Creați un ShapeNode pentru a reprezenta zona de joacă a jocului nostru. Aici șarpele se va mișca.
  • 6: Creează placa de joc. Această funcție inițializează o tonă de celule pătrate și le adaugă pe tabla de joc.

În continuare dorim să creăm o serie de celule pe care le vom folosi pentru a reda șarpele și punctele de pe ecran. Creați funcția createGameBoard din codul de mai jos, astfel încât să se potrivească cu figura H.

// creați o placă de joc, inițializați o serie de celule
private func createGameBoard (lățime: Int, înălțime: Int) {
    let cellWidth: CGFloat = 27.5
    lasa numRows = 40
    lasa numCols = 20
    var x = CGFloat (lățime / -2) + (lățime celular / 2)
    var y = CGFloat (înălțime / 2) - (lățime celular / 2)
    // buclați-vă prin rânduri și coloane, creați celule
    pentru i în 0 ... numRows - 1 {
        pentru j în 0 ... numCols - 1 {
            let cellNode = SKShapeNode (rectOf: CGSize (lățime: lățime celulă, înălțime: lățime celulă))
            cellNode.strokeColor = SKColor.black
            cellNode.zPosition = 2
            cellNode.position = CGPoint (x: x, y: y)
            // adăugați la o serie de celule - apoi adăugați pe tabla de joc
            gameArray.append ((nod: cellNode, x: i, y: j))
            gameBG.addChild (cellNode)
            // itera x
            x + = lățimea celulei
        }
        // resetare x, iterare y
        x = CGFloat (lățime / -2) + (lățime celular / 2)
        y - = lățimea celulei
    }
}
Figura H

Codul dvs. ar trebui să se potrivească cu cel de sus, la rularea jocului dvs. nu se va schimba nimic. Dacă doriți să vedeți placa de joc, cum ar fi în ecranul de ecran al simulatorului de mai sus, adăugați următorul cod funcției de început a jocului, astfel încât să se potrivească cu figura I.

bestScore.run (SKAction.move (până la: bottomCorner, durata: 0.4)) {
    self.gameBG.setScale (0)
self.currentScore.setScale (0)
self.gameBG.isHidden = fals
self.currentScore.isHidden = fals
self.gameBG.run (SKAction.scale (până la: 1, durata: 0.4))
self.currentScore.run (SKAction.scale (până la: 1, durata: 0.4))
}

O scurtă explicație a metodei createGameBoard înainte de a continua. Această metodă se buclează prin 40 de rânduri și 20 de coloane, pentru fiecare poziție rând / coloană creăm o nouă casetă pătrată sau „celulă nod” și adăugăm acest lucru la scenă. De asemenea, adăugăm acest cellNode într-un tablou „gameArray”, astfel încât să putem fixa cu ușurință un rând și o coloană spre celula corespunzătoare.

Figura I - Afișează noua placă de joc!

Crearea unei instanțe de joc

Avem acum un buton de lucru, o cutie plină cu cutii mai mici și câteva etichete. Cum putem transforma acest lucru într-un joc care este de fapt distractiv de jucat? Mai întâi vom avea nevoie de un obiect pentru a urmări locația unui „șarpe” pe ecran, astfel încât să ne putem deplasa. Deschideți clasa GameManager.swift și creați următoarele metode. Adăugați de asemenea această modificare (// 1) în funcția didMove (pentru a vedea: SKView) în GameScene.swift, astfel încât codul dvs. să corespundă cu Figura J.

// 1 - GameScene.swift
joc = GameManager (scenă: sine)
// 2 - GameManager.swift
class GameManager {
    
    var scenă: GameScene!
    init (scenă: GameScene) {
        self.scene = scenă
    }
}
Figura J

Făcând aceste modificări, spunem că GameManager trebuie să conțină o referință la clasa GameScene odată ce este inițializată. Acum clasa GameManager poate comunica cu GameScene apelând la scene.method_name. De exemplu, scene.startGame () ar rula funcția de pornire a jocului de sub controlul clasei GameManager.

Acum suntem gata să încărcăm jucătorul în GameView. Mai întâi adăugați următorul fragment de cod în fișierul dvs. GameScene.swift în metoda startGame () din parantezele bestScore.run () {} Această metodă va apela funcția initGame odată ce eticheta bestScore își va încheia SKAction.

// cod nou
self.game.initGame ()
Figura K

Acum accesați GameManager.swift și adăugați următoarele metode sub metoda init (scenă: GameScene), astfel încât codul să se potrivească cu Figura L.

// 1
func initGame () {
    // poziția jucătorului de pornire
    scene.playerPositions.append ((10, 10))
    scene.playerPositions.append ((10, 11))
    scene.playerPositions.append ((10, 12))
    renderChange ()
}
// 2
func renderChange () {
    for (nod, x, y) în scene.gameArray {
        if contine (a: scene.playerPositions, v: (x, y)) {
            node.fillColor = SKColor.cyan
        } altfel {
            node.fillColor = SKColor.clear
        }
    }
}
// 3
func contine (a: [(Int, Int)], v: (Int, Int)) -> Bool {
    let (c1, c2) = v
    for (v1, v2) într-un {if v1 == c1 && v2 == c2 {return true}}
    returnează fals
}
Figura L
  • 1: funcția initGame (). Aceasta adaugă 3 coordonate la tabloul de poziții PlayerScene,
  • 2: funcția renderChange (). Vom numi această metodă de fiecare dată când mutăm „șarpele” sau jucătorul. Aceasta face ca toate pătratele goale să fie clare și toate pătratele în care jucătorul este situat ca cyan.
  • 3: Aceasta este o funcție simplă care verifică dacă există un tuple (o structură rapidă de date care poate conține o combinație de tipuri sub formă de (Int, CGFloat, Int, String) ... etc), etc. Această funcție verifică dacă matricea playerPositions conține coordonatele introduse din tabloul de celule GameScene. Acesta nu este neapărat cel mai eficient mod de a face lucrurile, deoarece verificăm fiecare celulă în timpul fiecărei actualizări. Dacă doriți să vă contestați, încercați să actualizați codul, astfel încât să modifice doar pătratele din tabloul playerPozitions!

Mutarea playerului

Figura M

Avem acum jucătorul nostru redat pe ecran și capacitatea de a reda orice număr de poziții. Dacă adăugați mai multe coordonate la tabloul playerPozitions, atunci mai multe pătrate vor fi colorate cian. În timpul jocului vrem să mutăm constant „șarpele” într-o direcție până când jucătorul alunecă pe ecran pentru a schimba direcțiile. Iată o grilă care arată coordonatele sistemului nostru de grilă, astfel încât să puteți înțelege cu ușurință modul în care funcționează coordonatele din culise (Figura M).

După cum puteți vedea de etichetele scârboase, colțul din stânga sus este de 0,0, iar colțul din dreapta jos este de 39,19. Aceasta înseamnă că dacă dorim să ne mutăm jucătorul în direcțiile din stânga, dreapta, sus și jos, facem acest lucru aplicând următoarea algebră de bază (figura N).

Figura N

După cum vedeți, direcțiile din stânga / dreapta se potrivesc cu cea a unui plan tipic de coordonate; stânga este negativă și dreapta este pozitivă. Cu toate acestea, pentru a merge mai sus pe planul de coordonate vrem să scădem y și să coborâm în jos vrem să creștem y. Acest lucru se datorează faptului că bucla noastră pentru funcția createGameBoard a început în partea de sus și a redus.

Acum că ai înțeles direcția plăcii, poți implementa o metodă care mișcă jucătorul. Dacă deschideți fișierul GameScene.swift, veți observa o metodă utilă numită actualizare (_ currentTime: TimeInterval). În bucla de redare, funcția de actualizare este apelată o dată pe secundă. Acest lucru înseamnă că dacă aplicația dvs. rulează la 60 fps funcția este numită de 60 de ori pe secundă, dacă jocul rulează la 40 fps, se numește doar de 40 de ori pe secundă. În funcția de actualizare adăugați această linie de cod, astfel încât codul dvs. să se potrivească cu figura O.

// 1
game.update (ora: currentTime)
Figura O

După ce adăugați acest cod, ar trebui să apară o eroare roșie, pentru a repara acest lucru în fișierul dvs. GameManager.swift și adăugați aceste linii de cod astfel încât fișierul dvs. să se potrivească cu Figura P.

// 1
var nextTime: dublu?
var timeExtension: Double = 1
// 2
actualizare func (timp: dublu) {
    if nextTime == nil {
        nextTime = time + timeExtension
    } altfel {
        if time> = nextTime! {
            nextTime = time + timeExtension
            de imprimare (ora)
        }
    }
}
Figura P

La rularea aplicației, consola ar trebui să imprime o nouă dată în fiecare secundă. Vă prezentăm rapid ce face acest cod.

  • 1: inițializează două noi variabile. nextTime este intervalul nextTime vom imprima o declarație către consolă, timeExtension este cât timp vom aștepta între fiecare tipărire (1 secundă).
  • 2: Această funcție de actualizare se numește de 60 de ori pe secundă, dorim doar să actualizăm poziția jucătorului o dată pe secundă, astfel încât jocul să nu fie ridicol de rapid. Pentru a realiza acest lucru, verificăm dacă nextTime a fost setat. După cum puteți vedea din // 1, NextTime a fost inițializat pentru a fi o valoare opțională. „? „După Double le spune compilatorului rapid că dorim ca NextTime să fie dublu și că poate fi setat pe zero. Când se numește funcția de actualizare, mai întâi verificăm dacă nextTime a fost setat, dacă nu a fost setată, o setăm la ora curentă + the timeExtension (1 secundă). Odată ce ora actuală eclipsează „nextTime”, vom crește nextTime cu 1 secundă. Această funcție are acum o funcție de actualizare neregulată (de aproximativ 30–60 de ori / secundă) și produce o ieșire doar o dată pe secundă.

Avem acum o funcție care rulează o dată pe secundă, dacă doriți să creșteți viteza jocului dvs. pur și simplu reduceți timeExtension la o valoare mai mare de 0, dacă doriți să încetiniți jocul, atunci creșteți valoarea timeExtension. (Notă: „1” == 1 secundă pentru extinderea timpului).

Vrem acum să mutăm playerul pe ecran, să adăugăm următorul cod pentru ca fișierul dvs. să se potrivească cu Figura Q. De asemenea, eliminați linia „tipărire (timp)” din funcția de actualizare pe care tocmai am creat-o în GameManager.swift, acest lucru vă va spama consolă și a fost util cu adevărat numai pentru testarea validității codului dvs.

// 1
var playerDirection: Int = 1
// 2
updatePlayerPosition ()
// 3
private func updatePlayerPosition () {
    // 4
    var xChange = -1
    var yChange = 0
    // 5
    switch playerDirection {
        cazul 1:
            //stânga
            xChange = -1
            yChange = 0
            pauză
        cazul 2:
            //sus
            xChange = 0
            yChange = -1
            pauză
        cazul 3:
            //dreapta
            xChange = 1
            yChange = 0
            pauză
        cazul 4:
            //jos
            xChange = 0
            yChange = 1
            pauză
        Mod implicit:
            pauză
    }
    // 6
    if scene.playerPositions.count> 0 {
        var start = scene.playerPositions.count - 1
        în timp ce începe> 0 {
            scene.playerPositions [start] = scene.playerPositions [start - 1]
            start - = 1
        }
        scene.playerPositions [0] = (scene.playerPositions [0] .0 + yChange, scene.playerPositions [0] .1 + xChange)
    }
    // 7
    renderChange ()
}
Figura Q

După ce adăugați acest cod, jocul dvs. ar trebui să arate ca gif-ul din figura Q (setați jocul direcție pe 4 pentru a obține aceeași direcție de mișcare). Două lucruri mi-au ieșit imediat în evidență când am scris acest lucru; în primul rând, șarpele se mișcă dureros lent, poate ar trebui să creștem viteza jocului de la 1 secundă la 1/2 sau 1/4 de secundă. În al doilea rând, ce vom face când șarpele lovește un perete? În unele versiuni de șarpe, jucătorul se deformează pe ecran, în alte versiuni o coliziune cu un perete duce la moarte. Îmi place aspectul unei urzări a ecranului, așa că cred că vom folosi această metodă pentru acest joc. Acum o explicație pentru acest cod pe care tocmai l-ai scris:

  • 1: Creați o variabilă care este utilizată pentru a determina direcția curentă a jucătorului. În cod variabila este setată la 1, în gif-ul din figura Q am setat direcția pe 4. Schimbă această variabilă pentru a vedea toate direcțiile diferite.
  • 2: Am eliminat tipărirea (ora) și am înlocuit-o cu un apel la updatePlayerPosition (), în această iterație apelăm actualizarea în fiecare secundă.
  • 3: Această metodă mută playerul sau „șarpele” pe ecran.
  • 4: Setați variabile pentru a determina schimbarea pe care ar trebui să o facem la x / y din fața șarpelui.
  • 5: Aceasta este o declarație de comutare, preia intrarea playerPozition și modifică variabilele x / y în funcție de faptul că jucătorul se deplasează în sus, în jos, la stânga sau la dreapta.
  • 6: Acest bloc de cod deplasează pozițiile în față în tablou. Vrem să mișcăm partea din față a cozii în direcția corespunzătoare, apoi să mutați toate blocurile de coadă înainte în următoarea poziție.
  • 7: Reduceți modificările pe care le-am făcut în gama de poziții.

Se deformează șarpele în jurul ecranului

Avem acum un jucător în mișcare, muncă frumoasă! În primul rând vom dori să creștem viteza jocului, se pare că așteptarea 1 secundă este prea lent pentru a fi distractiv. Aceasta este o lecție pe care o veți învăța în designul jocului, există o mulțime de modificări și mici modificări pe care va trebui să le faceți pentru a perfecționa sentimentul jocului. Când lucrez la un proiect îmi petrec deseori majoritatea timpului făcând mici modificări pentru a regla sentimentul, trebuie să vă perfecționați mecanica pentru a construi un joc distractiv; odată ce ai o mecanică perfectă, poți lucra la adăugarea de adăugări fanteziste, precum particule și sunete.

Modificați variabila timeExtension la 0,15 și compilați-vă proiectul.

// 1 - GameManager.swift
var timeExtension: Double = 0,15

Acum putem începe să deformăm șarpele în jurul ecranului, să adăugăm următorul cod pentru ca proiectul dvs. să se potrivească cu figura R. Notă, acest cod este adăugat funcției updatePlayerPosition () din GameManager.swift pe care tocmai am scris-o.

// 1
if scene.playerPositions.count> 0 {
    let x = scene.playerPozitions [0] .1
    let y = scene.playerPozitii [0] .0
    daca y> 40 {
        scene.playerPozitii [0] .0 = 0
    } altceva dacă y <0 {
        scene.playerPozitii [0] .0 = 40
    } altceva dacă x> 20 {
       scene.playerPozitii [0] .1 = 0
    } altceva dacă x <0 {
        scene.playerPozitii [0] .1 = 20
    }
}
Figura R

La compilarea aplicației ecranul dvs. ar trebui să se potrivească cu gif-ul din figura R, am folosit playerDirection 4 în gif. Șarpele poate acum să se deformeze în fiecare parte a ecranului.

  • 1: Acest cod este destul de simplu, verifică dacă poziția capului șarpelui a trecut de partea de sus, de jos, de stânga sau de dreapta și apoi mută playerul în cealaltă parte a ecranului.

Controlul mișcării șarpelui folosind gesturi glisante

Jocul nostru se apropie, acum avem nevoie de o metodă pentru controlul direcției șarpelui. Pentru a implementa acest lucru, vom folosi gesturi glisante pentru a vă deplasa la stânga, la dreapta, în sus și în jos. Adăugați acest cod la fișierul dvs. GameScene.swift pentru a se potrivi cu Figura S.

// 1
let swipeRight: UISwipeGestureRecognizer = UISwipeGestureRecognizer (target: self, action: #selector (swipeR))
swipeRight.direction = .right
view.addGestureRecognizer (swipeRight)
let swipeLeft: UISwipeGestureRecognizer = UISwipeGestureRecognizer (țintă: auto, acțiune: #selector (swipeL))
swipeLeft.direction = .left
view.addGestureRecognizer (swipeLeft)
let swipeUp: UISwipeGestureRecognizer = UISwipeGestureRecognizer (țintă: auto, acțiune: #selector (swipeU))
swipeUp.direction = .up
view.addGestureRecognizer (swipeUp)
let swipeDown: UISwipeGestureRecognizer = UISwipeGestureRecognizer (țintă: auto, acțiune: #selector (swipeD))
swipeDown.direction = .down
view.addGestureRecognizer (swipeDown)
// 2
@objc func swipeR () {
    print ( "r")
}
@objc func swipeL () {
    print ( "l")
}
@objc func swipeU () {
    de imprimare ( "u")
}
@objc func swipeD () {
    print ( "d")
}
Figura S
  • 1: Adăugați gesturi glisante la funcția didMove (pentru a vizualiza: SKView).
  • 2: Creați funcții care sunt apelate atunci când utilizatorul intră într-un gest de glisare. „@Objc” înainte de funcție creează o funcție obiectiv-c, acest lucru este necesar pentru a fi apelat prin intermediul #selectorului în original UISwipeGestureRecognizer.

Testează-ți codul compilând aplicația și apoi glisând în toate cele patru direcții, consola ta ar trebui să imprime litera corespunzătoare pentru fiecare gest de glisare. Acum, că avem seturi de recunoaștere a gesturilor, trebuie să schimbăm direcția de mișcare a jucătorului, să înlocuim instrucțiunile de tipărire din interiorul funcțiilor swipe cu acest cod și să adăugăm cod la GameManager.swift, astfel încât proiectul să se potrivească cu Figura T.

// 1 - GameScene.swift
game.swipe (ID: 3)
game.swipe (ID: 1)
game.swipe (ID: 2)
game.swipe (ID: 4)
// 2 - GameManager.swift
func swipe (ID: Int) {
    if! (ID == 2 && playerDirection == 4) &&! (ID == 4 && playerDirection == 2) {
        if! (ID == 1 && playerDirection == 3) &&! (ID == 3 && playerDirection == 1) {
            playerDirection = ID
        }
    }
}
Figura TFigura T
  • 1: Odată detectat un gest glisant, clasa gameManager este notificată.
  • 2: Dacă o glisare nu este în contradicție cu direcția curentă, setați direcția jucătorului la intrarea de glisare. Dacă vă deplasați în jos, nu vă puteți deplasa imediat în sus. Dacă vă deplasați la stânga, nu vă puteți deplasa brusc la dreapta. În unele versiuni de șarpe care introduc o mișcare necorespunzătoare ca aceasta ar duce la moarte, dar în această versiune pur și simplu vom ignora intrările externe.

Adăugarea de puncte la joc și ajustarea scorului

Avem acum un sistem de meniu de lucru care deschide o placă de joc, o serie de celule, o serie de poziții ale jucătorului, un jucător care se poate deplasa pe ecran și se deformează în jurul marginilor și un recunoscător glisant pentru controale. Acum trebuie să adăugăm un mecanism de notare pentru ca jocul să fie distractiv. Acest mecanism va genera puncte aleatorii care vor crește scorul și vor prelungi urmele „șarpelui” sau jucătorului.

Primul pas: Generați un punct aleatoriu și redați-l pe ecran. Adăugați următorul cod la fișierul dvs. GameScene.swift, astfel încât să se potrivească cu Figura U.

// 1
var scorePos: CGPoint?
Figura U

Acum accesați fișierul dvs. GameManager.swift și adăugați următorul cod pentru a se potrivi cu figura V.

// 2
generateNewPoint ()
// 3
private func generateNewPoint () {
    let randomX = CGFloat (arc4random_uniform (19))
    let randomY = CGFloat (arc4random_uniform (39))
    scene.scorePos = CGPoint (x: randomX, y: randomY)
}
// 4
if scene.scorePos! = nil {
    if Int ((scene.scorePos? .x)!) == y && Int ((scene.scorePos? .y)!) == x {
        node.fillColor = SKColor.red
    }
}
Figura V

După rularea codului, simulatorul dvs. ar trebui să arate un pătrat roșu plasat la întâmplare.

  • 1: Inițializează o variabilă pentru poziția punctajului aleatoriu. „? „Indică faptul că acesta este nul (gol sau nu este încă setat) până când vom seta variabila mai târziu.
  • 2: Apelați funcția din interiorul funcției initGame () care va genera un nou punct aleatoriu.
  • 3: Această funcție generează o poziție aleatorie în limitele plăcii (20/40), matricile încep să fie numărate la 0, deci numărăm de la 0 la 19 și de la 0 la 39, acesta este un tablou 20x40.
  • 4: În interiorul buclei de redare verificăm dacă poziția nodului curent se potrivește cu cea a punctajului plasat la întâmplare, dacă avem o potrivire, atunci setăm culoarea pe roșu. Puteți modifica culoarea după bunul plac. Variabila care salvează poziția scorului este un CGPoint, aceasta înseamnă că trebuie să verificăm punct.x și point.y și să o comparăm cu locațiile x și y ale nodului curent. Rețineți că pozițiile x / y sunt răsucite în tabloul de noduri, de aceea comparăm x == y și y == x.

Acum trebuie să alocăm o variabilă care să dețină scorul jocului curent și să o repete odată ce jucătorul atinge un punct. Când jucătorul atinge un punct, trebuie să generăm un nou punct aleatoriu și să creștem lungimea „cozii” jucătorului.

Adăugați următorul cod la fișierul dvs. GameManager.swift, astfel încât să se potrivească cu Figura W.

// 1
var actual Scor: Int = 0
// 2
checkForScore ()
// 3
private func checkForScore () {
    if scene.scorePos! = nil {
        let x = scene.playerPozitions [0] .0
        let y = scene.playerPozitii [0] .1
        if Int ((scene.scorePos? .x)!) == y && Int ((scene.scorePos? .y)!) == x {
            Scor actual + = 1
            scene.currentScore.text = "Scor: \ (currentScore)"
            generateNewPoint ()
         }
     }
}
// 4
while contine (a: scene.playerPositions, v: (Int (randomX), Int (randomY))) {
    randomX = CGFloat (arc4random_uniform (19))
    randomY = CGFloat (arc4random_uniform (39))
}
Figura W
  • 1: Inițializează o variabilă pentru a urmări scorul curent din acest joc.
  • 2: Apelați funcția checkForScore () din funcția de actualizare, aceasta este apelată de fiecare dată când jucătorul se mișcă.
  • 3: Această funcție verifică dacă s-a setat un punctaj punct, dacă îl are apoi verifică capul șarpelui. Dacă șarpele atinge un punct, scorul este iterat, eticheta text care arată scorul este actualizată și se generează un punct nou.
  • 4: Am adăugat acest cod în metoda generateNewPoint () pentru a vă asigura că un punct nu este generat în corpul șarpelui. Pe măsură ce șarpele crește în lungime, vom fi mai probabil să ne confruntăm cu această problemă, deci acest bloc de cod ar trebui să rezolve problema.

La executarea codului dvs. veți observa că lovirea unui scor va genera un nou scor pe tabel și vă va itera eticheta. Acum trebuie să creștem lungimea șarpelui, astfel încât mecanica jocului să fie mai aproape de completare. Acest lucru se dovedește a fi incredibil de simplu, pur și simplu adăugați acest fragment de cod funcției dvs. CheckForScore (), astfel încât codul dvs. să se potrivească cu Figura X.

scene.playerPositions.append (scene.playerPositions.last!)
scene.playerPositions.append (scene.playerPositions.last!)
scene.playerPositions.append (scene.playerPositions.last!)
Figura X

Încheierea jocului

Acum trebuie să implementăm o metodă care să încheie jocul și să revină la un sistem de meniu. În jocul șarpelui, jocul se încheie odată ce jucătorul va intra în propria coadă. Putem obține acest efect prin implementarea următoarelor linii de cod în fișierul GameManager.swift. Asigurați-vă că codul dvs. se potrivește cu figura Y.

// 1
checkForDeath ()
// 2
private func checkForDeath () {
    if scene.playerPositions.count> 0 {
        var arrayOfPositions = scene.playerPositions
        let headOfSnake = arrayOfPositions [0]
        arrayOfPositions.remove (la: 0)
        if contine (a: arrayOfPositions, v: headOfSnake) {
            playerDirection = 0
        }
    }
}
// 3
if playerDirection! = 0 {
    playerDirection = ID
}
// 4
cazul 0:
    //mort
    xChange = 0
    yChange = 0
    pauză
Figura YFigura Y
  • 1: Apelați funcția checkForDeath ().
  • 2: verificați dacă capul jucătorului s-a ciocnit cu oricare dintre pozițiile cozii. Dacă un jucător a murit, setați playerDirection la 0.
Figura Z
  • 3: Dacă jucătorul a murit (playerDirection = 0), atunci nu permiteți noi gesturi de glisare ca intrări.
  • 4: Adăugați un caz nou în instrucțiunea switch în updatePlayerPosition (), dacă playerDirection a fost setat pe 0, atunci nu schimbați poziția capului. Acest lucru va permite pozițiile cozii să se îndepărteze încet din vedere.

După implementarea acestor modificări de cod, aplicația ar trebui să funcționeze ca în înregistrarea pe ecran (figura Z).

Când șarpele se ciocnește cu sine, jocul s-a încheiat, acum trebuie să construim o metodă pentru a reporni jocul și pentru a salva scorul ca punctaj.

Repornește jocul și salvează datele scorului mare

Acum am construit un joc de șarpe de lucru (în mare parte). Ultimii pași sunt la vedere! Avem nevoie de o metodă pentru a reporni jocul și pentru a reveni la meniu, de asemenea, trebuie să salvăm datele cu scorul mare pe dispozitiv dacă scorul acestui rundă a fost mai bun decât cel mai bun punctaj al dvs.

Mai întâi, să implementăm o metodă care revine la meniu după ce șarpele și-a încheiat animația de închidere. Adăugați următorul cod la fișierul dvs. GameManager.swift, astfel încât codul să se potrivească cu figura AA.

// 1
finishAnimation ()
// 2
private func finishAnimation () {
    if playerDirection == 0 && scene.playerPositions.count> 0 {
        var hasFinished = true
        let headOfSnake = scene.playerPositions [0]
        pentru poziția în scene.playerPositions {
            if headOfSnake! = position {
                hasFinished = fals
            }
         }
     dacă a terminat {
        print („joc final”)
        playerDirection = 4
        // animația s-a finalizat
        scene.scorePos = nil
        scene.playerPositions.removeAll ()
        renderChange ()
        // reveniți la meniu
        scene.currentScore.run (SKAction.scale (până la: 0, durata: 0.4) {
        self.scene.currentScore.isHidden = true
}
        scene.gameBG.run (SKAction.scale (până la: 0, durata: 0.4)) {
            self.scene.gameBG.isHidden = true
            self.scene.gameLogo.isHidden = fals
            self.scene.gameLogo.run (SKAction.move (până la: CGPoint (x: 0, y: (self.scene.frame.size.height / 2) - 200), durata: 0,5)) {
                 self.scene.playButton.isHidden = fals
                 self.scene.playButton.run (SKAction.scale (până la: 1, durata: 0.3))
                 self.scene.bestScore.run (SKAction.move (până la: CGPoint (x: 0, y: self.scene.gameLogo.position.y - 50), durata: 0.3))
               }
          }
          }
     }
}
Figura AA

Iată o explicație pentru această metodă:

  • 1: Apelați funcția finishAnimation ().
  • 2: Această funcție va verifica dacă este finalizată animația finală a șarpelui atunci când se va închide singură. Odată ce toate pozițiile din tabloul playerPositions se potrivesc, șarpele s-a micșorat la un pătrat. După aceasta, am setat playerDirection la 4 (anterior a fost setat la 0 indicând moartea) și apoi vom arăta obiectele din meniu. De asemenea, ascundem eticheta currentScore și obiectul gameBG (grila de pătrate).

Să adăugăm o metodă care salvează scorul mare pe dispozitiv, astfel încât la închiderea aplicației să nu pierdem datele noastre de scor mare. În noua metodă (finishAnimation ()) ați scris doar adăugați această linie de cod, astfel încât fișierul să se potrivească cu figura BB.

updateScore ()
Figura BB

Acum deschideți fișierul dvs. AppDelegate.swift și adăugați următoarele linii de cod, astfel încât proiectul să se potrivească cu figura CC. Acest fragment de cod folosește Setările implicite ale utilizatorului pentru a salva date în memoria dispozitivului. Dacă intenționați să construiți un proiect cu cantități uriașe de date stocate, acest lucru poate cauza o problemă, însă funcționează bine pentru lucruri simple, cum ar fi setările de comutare și variabilele.

let defaults = UserDefaults.standard
let defaultValue = ["cel mai bun scor": 0]
defaults.register (valorile implicite: defaultValue)
Figura CC

Acum reveniți la fișierul dvs. GameManager.swift și creați următoarea metodă, astfel încât codul dvs. să se potrivească cu figura DD. Acest bloc de cod verifică pur și simplu dacă scorul a bătut cel mai bun scor și se actualizează în consecință.

// 1
private func updateScore () {
     if currentScore> UserDefaults.standard.integer (forKey: "bestScore") {
          UserDefaults.standard.set (currentScore, forKey: "bestScore")
     }
     Scor actual = 0
     scene.currentScore.text = "Scor: 0"
     scene.bestScore.text = "Cel mai bun punctaj: \ (UserDefaults.standard.integer (pentruKey:" bestScore "))"
}
Figura DD

Deschide GameScene.swift și editează funcția initializeMenu () astfel încât fișierul să se potrivească cu Figura EE. Acest lucru se asigură că atunci când jocul se încarcă este afișat cel mai bun scor salvat, decât 0.

bestScore.text = "Cel mai bun punctaj: \ (UserDefaults.standard.integer (pentruKey:" bestScore "))"
Figura EE

După adăugarea acestui nou cod, scorul maxim va fi salvat în memoria dispozitivului dvs. odată cu închiderea aplicației.

Gânduri de închidere

Pentru a elimina informațiile pentru dezvoltatori din partea de jos a ecranului, deschideți fișierul GameViewController.swift și setați view.showFPS și view.showsNodeCount pe false.

Acum ați construit un joc iPhone întreg de la zero! Iată un videoclip care demonstrează produsul final.

Dacă v-a plăcut proiectul, luați în considerare verificarea aplicației mele iOS! Dacă aveți probleme sau întrebări, nu ezitați să-mi trimiteți un e-mail la shrader.gavin@gmail.com.

Ceea ce am construit astăzi abia zgârie suprafața complexității pe care motorul de joc SpriteKit trebuie să o ofere. În viitor, îmi propun să fac tutoriale care acoperă fizică, design la nivel și animații.