O introducere în GameplayKit Partea 1

Introducere

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:

  • entități și componente
  • mașini de stat

Cerințe preliminare

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.

1. Noțiuni de bază

Î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.

2. Entități și componente

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:

  • Un parinte Turn clasa care conține date comune, cum ar fi sănătatea, dimensiunea și puterea.
  • FireTowerIceTower, și HealTower clasele care vor moșteni de la Turn clasă.
  • În 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:

  • Am creat FireTowerIceTower, ș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.
  • Am creat, de asemenea, o BasicTower componenta care ar conține sănătate, dimensiune, putere etc..
  • Pentru a face față vindecării turnurilor dvs. într-o anumită rază, am adăuga a 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:

  • Ta FireTower și IceTower entitățile ar avea fiecare BasicTower și Direcționarea componentă legată de acesta.
  • Ta 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ă.

3. Mașini de stat

Î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:

  • un turn poate deveni invalid cand Activ si invers
  • un turn poate deveni Distrus când oricare dintre ele Activ sau invalid
  • un turn poate nu deveni 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:

  • Acesta implementează un inițializator simplu pentru a păstra o referință la nodul curent al jucătorului.
  • Are o implementare pentru 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.
  • Clasa include, de asemenea, o punere în aplicare pentru 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.

Concluzie

Î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.

Cod