Alături de toate noile caracteristici și cadre din cadrul iOS 9 și OS X El Capitan, cu lansările din acest an, Apple a creat, de asemenea, un cadru complet nou pentru dezvoltatorii de jocuri, GameplayKit. Cu ajutorul API-urilor grafice existente (SpriteKit, SceneKit și Metal), care facilitează crearea de jocuri minunate pe iOS și OS X, Apple a lansat acum GameplayKit pentru a facilita crearea de jocuri care Joaca bine. Acest nou cadru conține multe clase și funcționalități care pot fi folosite pentru a adăuga cu ușurință o logică complexă jocurilor dvs..
În acest prim tutorial, vă voi învăța despre două aspecte importante ale cadrului GameplayKt:
Acest tutorial necesită să fie difuzate Xcode 7 pe OS X Yosemite sau mai târziu. Dacă nu este necesar, este recomandat să aveți un dispozitiv fizic care rulează iOS 9 deoarece veți obține performanțe mult mai bune la testarea jocului bazat pe SpriteKit utilizat în acest tutorial.
În primul rând, va trebui să descărcați proiectul de pornire pentru această serie de tutoriale de la GitHub. Odată ce ați făcut acest lucru, deschideți proiectul în Xcode și rulați-l pe simulatorul iOS sau pe dispozitiv.
Veți vedea că este un joc foarte simplu în care controlați un punct albastru și navigați în jurul hărții. Când te ciocnești cu un punct roșu, pierzi două puncte. Când te ciocnești cu un punct verde, câștigi un punct. Când te ciocnești cu un punct galben, punctul tău devine înghețat pentru câteva secunde.
În acest stadiu, este un joc foarte de bază, dar pe tot parcursul seriei de tutorial și cu ajutorul GameplayKit, vom adăuga mult mai multă funcționalitate și elemente de joc.
Primul aspect major al noului cadru GameplayKit este un concept bazat pe structurarea codurilor entități și componente. Acesta funcționează permițându-vă, dezvoltatorului, să scrieți un cod comun care este folosit de mai multe tipuri de obiecte diferite în joc, păstrându-l bine organizat și ușor de gestionat. Conceptul de entități și componente are menirea de a elimina abordarea bazată pe moștenire comună pentru a împărtăși funcționalitatea comună între tipurile de obiecte. Cea mai ușoară modalitate de a înțelege acest concept este cu câteva exemple, astfel încât imaginați-vă următorul scenariu:
Construiești un joc de apărare în turn cu trei tipuri principale de turnuri, Foc, Gheaţă, și Vindeca. Cele trei tipuri de turnuri ar împărți câteva date comune, cum ar fi sănătatea, dimensiunea și puterea. Turnurile dvs. de Foc și de Gheață trebuie să poată să vizeze dușmanii care sosesc să tragă în timp ce Turnul vostru Heal nu are. Tot ce are nevoie de turnul vostru Heal este să-ți repari celelalte turnuri într-o anumită rază pe măsură ce primesc daune.
Având în vedere acest model de joc de bază, să vedem cum ar putea fi organizat codul pe care îl folosiți moştenire structura:
Turn
clasa care conține date comune, cum ar fi sănătatea, dimensiunea și puterea.FireTower
, IceTower
, și HealTower
clasele care vor moșteni de la Turn
clasă.HealTower
clasa, aveți logica responsabilă pentru vindecarea celorlalte turnuri într-o anumită rază.Până acum, această structură este în regulă, dar acum apare o problemă când trebuie să implementați capacitatea de direcționare a turnurilor de foc și de gheață. Trebuie doar să copiați și să inserați același cod în ambele FireTower
și IceTower
clase? Dacă trebuie să faceți modificări, va trebui să vă schimbați codul în mai mult de un loc, ceea ce este obositor și greșit. În plus, ce se întâmplă dacă doriți să adăugați un nou tip de turn care necesită și această funcție de direcționare. Copiați și lipiți-o a treia oară?
Cea mai bună cale pare a fi aceea de a pune această logică de direcționare în părinte Turn
clasă. Acest lucru vă va permite să aveți doar o copie a codului care trebuie doar să fie editat într-un singur loc. Adăugarea acestui cod aici, cu toate acestea, ar face Turn
clasa este mult mai mare și mai complicată decât trebuie să fie când nu toate subclasele sale au nevoie de această funcționalitate. Dacă doriți, de asemenea, să adăugați mai multe funcționalități partajate între tipurile de turnuri, dvs. Turn
clasa ar deveni treptat mai mare și mai mare, ceea ce ar face cu greu să lucreze cu.
După cum puteți vedea, în timp ce este posibil să creați un model de joc bazat pe moştenire, ea poate foarte rapid și ușor să devină neorganizată și dificil de gestionat.
Acum, să vedem cum ar putea fi structurat același model de joc entități și componente:
FireTower
, IceTower
, și HealTower
entități. Pentru mai multe tipuri de turnuri pe care doriți să le adăugați ulterior, ar putea fi create mai multe entități.BasicTower
componenta care ar conține sănătate, dimensiune, putere etc..cicatrizare
component.Direcționarea
componenta ar conține codul necesar pentru a viza inamicii care sosesc.Folosind GameplayKit și această structură, veți avea apoi un tip de entitate unic pentru fiecare tip de turn în joc. Pentru fiecare entitate individuală, puteți adăuga componentele dorite. De exemplu:
FireTower
și IceTower
entitățile ar avea fiecare BasicTower
și Direcționarea
componentă legată de acesta.HealTower
entitatea ar avea atât o BasicTower
și a cicatrizare
component.După cum puteți vedea, prin utilizarea unei structuri bazate pe entitate și componente, modelul dvs. de joc este acum mult mai simplu și mai versatil. Logica dvs. de direcționare trebuie să fie scrisă o singură dată și doar link-uri către entități de care are nevoie. De asemenea, datele despre turnul de bază pot fi totuși ușor de distribuit între toate turnurile dvs., fără a vă grăbi toate celelalte funcții comune.
Un alt lucru extraordinar în legătură cu această structură bazată pe entitate și pe componente este aceea că componentele pot fi adăugate și eliminate de la entități ori de câte ori doriți. De exemplu, dacă doriți ca turnurile voastre Heal să fie dezactivate în anumite condiții, puteți să le eliminați cicatrizare
componentă din entitatea dvs. până când sunt îndeplinite condițiile corecte. De asemenea, dacă v-ați dorit ca unul dintre turnurile dvs. de foc să obțină o capacitate temporară de vindecare, ați putea să adăugați a cicatrizare
componente pentru dvs. FireTower
pentru o anumită perioadă de timp.
Acum, că vă simțiți confortabil cu conceptul unei structuri de model de joc bazate pe entitate și pe componente, să creăm una în cadrul jocului propriu. În Xcode's Inspector de dosar, găsi entităţile dosar în cadrul proiectului dvs. Pentru confort, există deja trei clase de entități pentru dvs., dar acum veți crea o entitate nouă de la zero.
Alege Fișier> Nou> Fișier ... sau apăsați Command-N pentru a crea o clasă nouă. Asigurați-vă că ați selectat Cocoa Touch Class șablon din iOS> Sursă secțiune. Denumiți clasa Jucător și să o facă o subclasă de GKEntity
.
Veți vedea că imediat după deschiderea noului fișier Xcode va afișa o eroare. Pentru a remedia această problemă, adăugați următoarea declarație de import sub cea existentă import UIKit
afirmație:
import GameplayKit
Intoarce-te la PlayerNode.swift și adăugați următoarea proprietate la PlayerNode
clasă:
var entity = Player ()
Apoi, navigați la Componente în proiectul dvs. Xcode și creați o nouă clasă așa cum ați făcut înainte. De data aceasta, numiți clasa FlashingComponent și să o facă o subclasă de GKComponent
așa cum se arată mai jos.
Componenta pe care tocmai ați creat-o va face față mișcării vizuale a punctului nostru albastru atunci când este lovită de un punct roșu și se află în starea sa invulnerabilă. Înlocuiți conținutul FlashingComponent.swift cu urmatoarele:
Import Import UIKit Import SpriteKit Clasă GameplayKit FlashingComponent: GKComponent var nodeToFlash: SKNode! func startFlashing () let fadeAction = SKAction.sequence ([SKAction.fadeOutWithDuration (0.75), SKAction.fadeInWithDuration (0.75)]) nodeToFlash.runAction (SKAction.repeatActionForever (fadeAction), cuKey: "flash") deinit nodeToFlash. removeActionForKey ("flash")
Implementarea ține pur și simplu o referință la un SKNode
obiectul și repeta se estompează și se estompează acțiunile în ordine, atâta timp cât componenta este activă.
Intoarce-te la GameScene.swift și adăugați următorul cod undeva în didMoveToView (_ :)
metodă:
// Adăugarea Componentului permite flash = FlashingComponent () flash.nodeToFlash = playerNod flash.startFlashing () playerNode.entity.addComponent (flash)
Creăm a FlashingComponent
obiect și setați-l pentru a efectua intermitent pe punctul jucătorului. Ultimul rând adaugă componenta la entitate pentru ao menține activă și executată.
Construiți și executați aplicația. Veți vedea acum că punctul tău albastru încetinește încet și încet în mod repetat.
Înainte de a trece mai departe, ștergeți codul pe care tocmai l-ați adăugat din didMoveToView (_ :)
metodă. Mai târziu, veți adăuga acest cod înapoi, dar numai atunci când punctul tău albastru intră în starea sa invulnerabilă.
În GameplayKit, aparatele de stat oferă o modalitate prin care puteți identifica și efectua cu ușurință sarcini bazate pe curent stat a unui anumit obiect. Desenând de la exemplul anterior al apărării turnului, ar putea fi incluse câteva stări posibile pentru fiecare turn Activ
, invalid
, și Distrus
. Un avantaj major al mașinilor de stat este acela că puteți specifica la care state se poate muta un alt stat. Cu cele trei stări de exemplu menționate mai sus, folosind o mașină de stat, ați putea seta mașina de stat în sus astfel încât:
invalid
cand Activ
si inversDistrus
când oricare dintre ele Activ
sau invalid
Activ
sau invalid
odată ce a fost Distrus
În jocul pentru acest tutorial, îl vom păstra foarte simplu și vom avea doar unul normal și invulnerabil stat.
În proiectul tău Mașină de stat folder, creați două clase noi. Nume-le NormalState
și InvulnerableState
respectiv, ambele fiind o subclasă a GKState
clasă.
Înlocuiți conținutul NormalState.swift cu urmatoarele:
import Import UIKit Import SpriteKit Clasa GameplayKit NormalState: GKState var node: PlayerNode init (cuNode: PlayerNode) node = cuNode suprascrie func esteValidNextState (stateClass: AnyClass) -> Bool switch stateClass caz invulnerableState.Type: return true true : return false suprascrie func didEnterWithPreviousState (precedentState: GKState?) if let _ = previousState as? InvulnerableState node.entity.removeComponentForClass (FlashingComponent) node.runAction (SKAction.fadeInWithDuration (0.5))
NormalState
clasa conține următoarele:
isValidNextState (_ :)
metodă. Implementarea acestei metode returnează o valoare booleană, indicând dacă clasa de stare curentă poate sau nu se poate muta la clasa de stare furnizată de parametrul metodei.didEnterWithPreviousState (_ :)
metoda de retur. În implementarea metodei, verificăm dacă starea anterioară a fost InvulnerableState
și, dacă este adevărat, eliminați componenta intermitentă de la entitatea playerului.Acum deschis InvulnerableState.swift și înlocuiți conținutul cu următoarele:
import Importul UIKit Clasa GameplayKit InvulnerableState: GKState var node: PlayerNode init (cuNode: PlayerNode) node = cuNode suprascrie func esteValidNextState (stateClass: AnyClass) -> Bool switch stateClass caz este NormalState.Type: return true true: return false suprascrie func didEnterWithPreviousState (previousState: GKState?) dacă let _ = previousState as? NormalState // Adăugarea componentei lasă flash = FlashingComponent () flash.nodeToFlash = nod flash.startFlashing () node.entity.addComponent (flash)
InvulnerableState
clasa este foarte asemănătoare cu NormalState
clasă. Principala diferență este că, la intrarea în această stare, adăugați componentul intermitent la entitatea jucătorului, în loc să îl eliminați.
Acum că clasele de stat sunt atât complete, deschise PlayerNode.swift din nou și adăugați următoarele linii la PlayerNode
clasă:
var stateMachine: GKStateMachine! func enterNormalState () self.stateMachine.enterState (NormalState)
Acest fragment de cod adaugă o nouă proprietate la PlayerNode
clasa și pune în aplicare o metodă convenabilă pentru a reveni la starea normală.
Acum deschis GameScene.swift și, la sfârșitul anului didMoveToView (_ :)
, adăugați următoarele două linii:
playerNode.stateMachine = GKStateMachine (stări: [NormalState (cuNode: playerNode), InvulnerableState (cuNode: playerNode)]) playerNode.stateMachine.enterState (NormalState)
În aceste două linii de cod, creăm un nou GKStateMachine
cu cele două state și spuneți-i să intre NormalState
.
În cele din urmă, înlocuiți punerea în aplicare a directivei handleContactWithNode (_ :)
metodă a GameScene
clasa cu următoarea implementare:
func handleContactWithNode (contact: ContactNode) dacă contactul este PointNode NSNotificationCenter.defaultCenter () postNotificationName ("updateScore", obiect: self, userInfo: ["score": 1]) altceva dacă contactul este RedEnemyNode && playerNode.stateMachine. starea curenta! este normalState NSNotificationCenter.defaultCenter () .Name.stateMachine.enterState (InvulnerableState) playerNode.performSelector ("enterNormalState", cuObject: null, userInfo: ["score" afterDelay: 5.0) altfel dacă contactul este YellowEnemyNode && playerNode.stateMachine.currentState! este NormalState auto.playerNode.enabled = false contact.removeFromParent ()
Atunci când punctul albastru al jucătorului se ciocnește cu un punct inamic roșu, jucătorul va intra în InvulnerableState
stare timp de cinci secunde și apoi reveniți la NormalState
stat. Verificăm, de asemenea, care este starea actuală a jucătorului și efectuează numai orice logică a inamicului dacă este NormalState
stat.
Construiți și rulați aplicația ultima oară și deplasați harta până când găsiți un punct roșu. Când te ciocnești cu punctul roșu, vei vedea că punctul tău albastru intră în starea lui invulnerabilă și clipește timp de cinci secunde.
În acest tutorial, v-am prezentat două aspecte importante ale cadrului GameplayKit, entități și componente, și mașini de stat. Te-am arătat cum poți folosi entități și componente pentru a structura modelul tău de joc și pentru a ține totul organizat. Folosirea componentelor este o modalitate foarte ușoară de a partaja funcționalitatea între obiectele din jocurile tale.
De asemenea, v-am arătat elementele de bază ale mașinilor de stat, inclusiv modul în care puteți specifica care stări poate trece o anumită stare, precum și codul de execuție atunci când este introdusă o anumită stare.
Rămâi acordat pentru cea de-a doua parte a acestei serii unde vom lua acest joc la un alt nivel prin adăugarea unor informații inteligente, mai bine cunoscute sub numele de AI. AI va permite punctelor inamice să vizeze jucătorul și să găsească cea mai bună cale pentru a ajunge la player.
Ca întotdeauna, dacă aveți orice comentarii sau întrebări, lăsați-le în comentariile de mai jos.