En introduksjon til GameplayKit Del 1

Introduksjon

Sammen med alle de nye funksjonene og rammene i iOS 9 og OS X El Capitan, med årets utgivelser skapte Apple også et helt nytt rammeverk for spillutviklere, GameplayKit. Med eksisterende grafikk-APIer (SpriteKit, SceneKit og Metal) gjør det enkelt å lage flotte spill på iOS og OS X, har Apple nå sluppet GameplayKit for å gjøre det enkelt å lage spill som spille vi vil. Dette nye rammeverket inneholder mange klasser og funksjoner som kan brukes til å legge til komplisert logikk på spillene dine.

I denne første opplæringen vil jeg lære deg om to hovedaspekter av GameplayKt-rammeverket:

  • enheter og komponenter
  • statlige maskiner

Forutsetninger

Denne opplæringen krever at du kjører Xcode 7OS X Yosemite eller senere. Selv om det ikke kreves, anbefales det at du har en fysisk enhet som kjører iOS 9 som du vil få mye bedre ytelse når du tester SpriteKit-basert spill som brukes i denne opplæringen.

1. Komme i gang

Du må først laste ned startprosjektet for denne serien av opplæringsprogrammer fra GitHub. Når du har gjort dette, åpner du prosjektet i Xcode og kjører det på enten iOS-simulatoren eller enheten.

Du vil se at det er et veldig grunnleggende spill der du styrer en blå prikk og navigerer rundt kartet. Når du kolliderer med en rød prikk, mister du to poeng. Når du kolliderer med en grønn prikk, får du ett poeng. Når du kolliderer med en gul prikk, blir din egen prikk frossen i noen sekunder.

På dette stadiet er det et veldig grunnleggende spill, men i løpet av denne opplæringsserien, og med hjelp av GameplayKit, kommer vi til å legge til i mye mer funksjonalitet og spillelementer.

2. Enheter og komponenter

Det første hovedaspektet av det nye GameplayKit-rammeboken er et kode-struktureringskonsept basert på enheter og komponenter. Det fungerer ved å tillate deg, utvikleren, å skrive felles kode som brukes av mange forskjellige objekttyper i spillet ditt, samtidig som det holder det godt organisert og håndterbart. Begrepet enheter og komponenter er ment å eliminere den felles arvebaserte tilnærmingen for å dele felles funksjonalitet mellom objekttyper. Den enkleste måten å forstå dette konseptet er med noen eksempler, så forestill deg følgende scenario:

Du bygger et tårnforsvarsspill med tre hovedtyper av tårn, Brann, Is, og Helbrede. De tre tårnene vil dele noen vanlige data, for eksempel helse, størrelse og styrke. Ditt brann- og istårn må kunne målrette innkommende fiender til å skyte på mens ditt Heal Tower ikke gjør det. Alt som Heal Tower må gjøre, er å reparere de andre tårnene i en bestemt radius ettersom de får skade.

Med denne grunnleggende spillmodellen i tankene, la oss se hvordan koden din kunne organiseres ved hjelp av en arv struktur:

  • En forelder Tårn klassen inneholder vanlige data som helse, størrelse og styrke.
  • firetowerIceTower, og HealTower klasser som ville arve fra Tårn klasse.
  • HealTower klasse, har du logikken ansvarlig for å helbrede de andre tårnene i en bestemt radius.

Så langt er denne strukturen ok, men et problem oppstår nå når du må implementere brann- og istårnens målrettingsevne. Kopierer du og limer inn samme kode i begge dine firetower og IceTower klasser? Hvis du trenger å gjøre noen endringer, må du endre koden på mer enn ett sted, noe som er kjedelig og feilaktig. På toppen av dette, hva skjer hvis du vil legge til i en ny tårnstype som også trenger denne målrettingsfunksjonen. Kopierer du og limer det inn en tredje gang?

Den beste måten å være å sette denne målrettingslogikken i foreldrene Tårn klasse. Dette vil tillate deg å bare ha en kopi av koden som bare må redigeres på ett sted. Å legge til denne koden her, ville imidlertid gjøre Tårn klassen er mye større og mer komplisert enn den må være når ikke alle sine underklasser trenger den funksjonaliteten. Hvis du også vil legge til mer delt funksjonalitet mellom tårntyper, din Tårn klassen vil etter hvert bli større og større, noe som vil gjøre det vanskelig å jobbe med.

Som du kan se, er det mulig å lage en spillmodell basert på arv, det kan veldig raskt og enkelt bli uorganisert og vanskelig å håndtere.

Nå, la oss se hvordan denne samme spillmodellen kunne struktureres ved hjelp av enheter og komponenter:

  • Vi ville skape firetowerIceTower, og HealTower enheter. Flere enheter kan bli opprettet for flere tårntyper du vil legge til senere.
  • Vi vil også lage en BasicTower komponent som vil inneholde helse, størrelse, styrke, osv.
  • For å håndtere helingen av tårnene dine innen en viss radius, legger vi til en Helbredelse komponent.
  • EN Rettet mot komponenten vil inneholde koden som trengs for å målrette innkommende fiender.

Ved å bruke GameplayKit og denne strukturen vil du da ha en unik entitetstype for hver tårntype i spillet ditt. Til hver enkelt enhet kan du legge til de ønskede komponentene du vil ha. For eksempel:

  • Din firetower og IceTower enheter vil hver ha en BasicTower og Rettet mot komponent knyttet til den.
  • Din HealTower Enhet ville ha begge a BasicTower og a Helbredelse komponent.

Som du ser, ved å bruke en enhet- og komponentbasert struktur, er spillmodellen din nå enklere og mer allsidig. Målrettingslogikken din trenger bare å skrives en gang og bare lenker til de enhetene den trenger. På samme måte kan de grunnleggende tårndataene dine fortsatt enkelt deles mellom alle tårnene dine uten at du fyller opp alle de andre vanlige funksjonalitetene dine.

En annen flott ting om denne entitets- og komponentbaserte strukturen er at komponenter kan legges til og fjernes fra enheter når du vil. For eksempel, hvis du vil at Heal tårnene skal deaktiveres under visse forhold, kan du bare fjerne Helbredelse komponent fra enheten din til de riktige forholdene er oppfylt. På samme måte, hvis du ønsket at en av dine branntårn skulle få en midlertidig helbredelsesevne, kunne du bare legge til en Helbredelse komponent til din firetower enhet for en bestemt tid.

Nå som du er komfortabel med konseptet med en entitets- og komponentbasert spillmodellstruktur, la oss lage en i vårt eget spill. I Xcode er Filinspektør, Finn Entities mappe i prosjektet ditt. For enkelhets skyld er det allerede tre entitetsklasser for deg, men du skal nå lage en ny enhet fra bunnen av.

Velge Fil> Ny> Fil ... eller trykk Kommando-N å lage en ny klasse. Pass på å velge Cocoa Touch Class mal fra iOS> Kilde seksjon. Gi klassen navn Spiller og gjør det til en underklasse av GKEntity.

Du vil se at umiddelbart ved åpningen av den nye filen vil Xcode vise en feil. For å fikse dette, legg til følgende importoppgave under det eksisterende importere UIKit uttalelse:

importer GameplayKit

Gå tilbake til PlayerNode.swift og legg til følgende eiendom til PlayerNode klasse:

var enhet = spiller ()

Deretter navigerer du til komponenter mappe i Xcode-prosjektet ditt og opprett en ny klasse som du gjorde før. Denne gangen, navnet på klassen FlashingComponent og gjør det til en underklasse av GKComponent som vist under.

Komponenten du nettopp har opprettet, skal håndtere den visuelle blinkingen av vår blå prikk når den rammes av en rød prikk og er i uskadelig tilstand. Erstatt innholdet i FlashingComponent.swift med følgende:

importere UIKit import SpriteKit import GameplayKit klasse FlashingComponent: GKComponent var nodeToFlash: SKNode! func startFlashing () la fadeAction = SKAction.sequence ([SKAction.fadeOutWithDuration (0.75), SKAction.fadeInWithDuration (0.75)]) nodeToFlash.runAction (SKAction.repeatActionForever (fadeAction), medKey: "flash") deinit nodeToFlash. removeActionForKey ("flash")

Implementeringen holder bare en henvisning til en SKNode Objekt og gjentakelser fade inn og fade ut handlinger i rekkefølge så lenge komponenten er aktiv.

Gå tilbake til GameScene.swift og legg til følgende kode et sted i didMoveToView (_ :) metode:

// Legge til komponent la flash = FlashingComponent () flash.nodeToFlash = playerNode flash.startFlashing () playerNode.entity.addComponent (flash)

Vi lager en FlashingComponent objekt og sett det opp for å utføre blinkende på spillerens prikk. Den siste linjen legger deretter komponenten til enheten for å holde den aktiv og utført.

Bygg og kjør appen din. Du vil nå se at din blå prikk forsvinner sakte inn og ut flere ganger.

Før du går videre, slett koden du nettopp lagt til fra didMoveToView (_ :) metode. Senere vil du legge til denne koden tilbake, men bare når den blå prikken kommer inn i sin uskadelige tilstand.

3. State Machines

I GameplayKit gir statlige maskiner en måte for deg å enkelt identifisere og utføre oppgaver basert på gjeldende stat av en bestemt gjenstand. Tegning fra det tidligere tårnforsvaret, noen mulige stater for hvert tårn kunne inkludere Aktiv, Funksjonshemmet, og ødelagt. En stor fordel ved statlige maskiner er at du kan angi hvilke stater en annen stat kan flytte til. Med de tre eksempelstatene som er nevnt ovenfor, ved hjelp av en statlig maskin, kan du sette statlig maskin opp slik at:

  • et tårn kan bli Funksjonshemmet når Aktiv og vice versa
  • et tårn kan bli ødelagt når heller ikke Aktiv eller Funksjonshemmet
  • et tårn kan ikke bli til Aktiv eller Funksjonshemmet når det har vært ødelagt

I spillet for denne opplæringen skal vi holde det veldig enkelt og bare ha a normal og usårbar stat.

I prosjektets Stat Machine mappe, opprett to nye klasser. Gi dem navn NormalState og InvulnerableState henholdsvis, med begge er en underklasse av GKState klasse.

Erstatt innholdet i NormalState.swift med følgende:

importere UIKit import SpriteKit import GameplayKit klasse NormalState: GKState var node: PlayerNode init (withNode: PlayerNode) node = withNode overstyr func isValidNextState (stateClass: AnyClass) -> Bool switch stateClass tilfelle er InvulnerableState.Type: returner true default : return false overstyre func didEnterWithPreviousState (forrigeState: GKState?) hvis la _ = forrigeState som? InvulnerableState node.entity.removeComponentForClass (FlashingComponent) node.runAction (SKAction.fadeInWithDuration (0.5))

De NormalState klassen inneholder følgende:

  • Den implementerer en enkel initialiserer for å holde en referanse til gjeldende spillerens knutepunkt.
  • Den har en implementering for isValidNextState (_ :) metode. Gjennomføringen av denne metoden gir en boolsk verdi, som indikerer om nåværende tilstandsklasse kan flytte til tilstandsklassen gitt av metoden parameter.
  • Klassen inneholder også en implementering for didEnterWithPreviousState (_ :) tilbakeringingsmetode. I metodens implementering kontrollerer vi om forrige tilstand var InvulnerableState stat og, hvis sant, fjern den blinkende komponenten fra spillerens enhet.

Nå åpen InvulnerableState.swift og erstatt innholdet med følgende:

importere UIKit import GameplayKit klasse InvulnerableState: GKState var node: PlayerNode init (withNode: PlayerNode) node = withNode overstyr func isValidNextState (stateClass: AnyClass) -> Bool switch stateClass tilfelle er NormalState.Type: return true true default false overstyre func didEnterWithPreviousState (forrigeState: GKState?) hvis la _ = forrigeState som? NormalState // Adding Component la flash = FlashingComponent () flash.nodeToFlash = node flash.startFlashing () node.entity.addComponent (flash)

De InvulnerableState klassen er veldig lik den NormalState klasse. Hovedforskjellen er at ved å legge inn denne tilstanden legger du til den blinkende komponenten til spillerens enhet i stedet for å fjerne den.

Nå som statsklassene dine er begge ferdige, åpne PlayerNode.swift igjen og legg til følgende linjer til PlayerNode klasse:

var stateMachine: GKStateMachine! func enterNormalState () self.stateMachine.enterState (NormalState)

Denne kodestykket legger til en ny egenskap i PlayerNode klasse og implementerer en praktisk metode for å gå tilbake til normal tilstand.

Nå åpen GameScene.swift og på slutten av didMoveToView (_ :) metode, legg til følgende to linjer:

playerNode.stateMachine = GKStateMachine (stater: [NormalState (withNode: playerNode), InvulnerableState (withNode: playerNode)]) playerNode.stateMachine.enterState (NormalState)

I disse to kodelinjene oppretter vi en ny GKStateMachine med de to statene og fortelle det å komme inn i NormalState.

Endelig, erstatt implementeringen av handleContactWithNode (_ :) metode av GameScene klasse med følgende implementering:

(kontakt: ContactNode) hvis kontakt er PointsNode NSNotificationCenter.defaultCenter (). postNotificationName ("updateScore", objekt: selv, brukerInfo: ["score": 1]) ellers hvis kontakten er RedEnemyNode && playerNode.stateMachine. nåværende situasjon! er NormalState NSNotificationCenter.defaultCenter (). postNotificationName ("updateScore", objekt: selv, brukerInfo: ["score": -2]) playerNode.stateMachine.enterState (InvulnerableState) playerNode.performSelector ("enterNormalState", withObject: nil, afterDelay: 5.0) ellers hvis kontakten er YellowEnemyNode && playerNode.stateMachine.currentState! er NormalState self.playerNode.enabled = false contact.removeFromParent ()

Når spillerens blå prikk kolliderer med en rød fiende punkt, vil spilleren komme inn i InvulnerableState angi i fem sekunder og deretter tilbake til NormalState stat. Vi kontrollerer også hva den nåværende tilstanden til spilleren er, og utfører bare en fiendtlig logikk hvis den er NormalState stat.

Bygg og kjør appen din en gang, og flytt rundt kartet til du finner en rød prikk. Når du kolliderer med den røde prikken, vil du se at den blå prikken din kommer i uskadelig tilstand og blinker i fem sekunder.

Konklusjon

I denne opplæringen introduserte jeg deg til to av de store aspektene av GameplayKit-rammen, enheter og komponenter, og statlige maskiner. Jeg viste deg hvordan du kan bruke enheter og komponenter til å strukturere spillmodellen og holde alt organisert. Bruke komponenter er en veldig enkel måte å dele funksjonalitet mellom objekter i spillene dine.

Jeg viste deg også grunnleggende for statlige maskiner, inkludert hvordan du kan angi hvilke stater en bestemt stat kan overføre til, så vel som å utføre kode når en bestemt stat er angitt.

Hold deg innstilt for den andre delen av denne serien der vi skal ta dette spillet til et annet nivå ved å legge til litt kunstig intelligens, bedre kjent som AI. AI vil aktivere fiendens prikker for å målrette mot spilleren og finne den beste banen for å nå spilleren.

Som alltid, hvis du har noen kommentarer eller spørsmål, la dem være i kommentarene nedenfor.