En introduksjon til GameplayKit Del 3

Dette er den tredje delen av En introduksjon til GameplayKit. Hvis du ikke har gått gjennom den første delen og den andre delen, anbefaler jeg at du leser disse veiledningene før du fortsetter med denne.

Introduksjon

I denne tredje og siste opplæringen skal jeg lære deg om to andre funksjoner du kan bruke i dine egne spill:

  • tilfeldige verdi generatorer
  • styresystemer

I denne opplæringen vil vi først bruke en av GameplayKits tilfeldige verdi generatorer for å optimalisere vår første fiende gyte algoritme. Vi vil da implementere et grunnleggende regelsystem i kombinasjon med en annen tilfeldig distribusjon for å håndtere respekterende oppførsel av fiender.

For denne opplæringen kan du bruke kopien av det gjennomførte prosjektet fra den andre opplæringen eller laste ned en ny kopi av kildekoden fra GitHub.

1. Tilfeldige verdier Generatorer

Tilfeldige verdier kan genereres i GameplayKit ved å bruke hvilken som helst klasse som samsvarer med GKRandom protokoll. GameplayKit gir fem klasser som samsvarer med denne protokollen. Disse klassene inneholder tre tilfeldige kilder og to tilfeldige distribusjoner. Hovedforskjellen mellom tilfeldige kilder og tilfeldige distribusjoner er at fordelingene bruker en tilfeldig kilde til å produsere verdier innenfor et bestemt område og kan manipulere tilfeldige verdier på ulike måter.

De nevnte klassene er gitt av rammen slik at du kan finne den rette balansen mellom ytelse og tilfeldighet for spillet ditt. Noen random value genererende algoritmer er mer komplekse enn andre og følgelig påvirke ytelsen.

For eksempel, hvis du trenger et tilfeldig tall som genereres hver ramme (seksti ganger per sekund), vil det være best å bruke en av de raskere algoritmer. I kontrast, hvis du bare sjelden genererer en tilfeldig verdi, kan du bruke en mer kompleks algoritme for å gi bedre resultater.

De tre tilfeldige kildeklassene som tilbys av GameplayKit-rammen er GKARC4RandomSourceGKLinearCongruentialRandomSource, og GKMersenneTwisterRandomSource.

GKARC4RandomSource

Denne klassen bruker ARC4-algoritmen og passer for de fleste formål. Denne algoritmen fungerer ved å produsere en rekke tilfeldige tall basert på et frø. Du kan initialisere en GKARC4RandomSource med et bestemt frø hvis du må replikere tilfeldig oppførsel fra en annen del av spillet ditt. En eksisterende kildes frø kan hentes fra sin frø skrivebeskyttet eiendom.

GKLinearCongruentialRandomSource

Denne tilfeldige kildeklassen bruker den grunnleggende lineære kongruensielle generatoralgoritmen. Denne algoritmen er mer effektiv og fungerer bedre enn ARC4-algoritmen, men genererer også verdier som er mindre tilfeldige. Du kan hente en GKLinearCongruentialRandomSource objektets frø og opprett en ny kilde med den på samme måte som a GKARC4RandomSource gjenstand.

GKMersenneTwisterRandomSource

Denne klassen bruker Mersenne Twister algoritmen og genererer de mest tilfeldige resultatene, men det er også minst effektiv. Akkurat som de to andre tilfeldige kildeklassene, kan du hente a GKMersenneTwisterRandomSource objektets frø og bruk det til å skape en ny kilde.

De to tilfeldige distribusjonsklassene i GameplayKit er GKGaussianDistribution og GKShuffledDistribution.

GKGaussianDistribution

Denne distribusjonstypen sikrer at de genererte tilfeldige verdiene følger en Gauss-distribusjon, også kjent som en normal fordeling. Dette betyr at de fleste av de genererte verdiene vil falle midt i det området du angir.

For eksempel, hvis du setter opp en GKGaussianDistribution objekt med en minimumsverdi på 1, en maksimumsverdi på 10, og en standardavvik av 1, omtrent 69% av resultatene ville være enten 4, 5, eller 6. Jeg vil forklare denne fordelingen mer detaljert når vi legger til en til vårt spill senere i denne opplæringen.

GKShuffledDistribution

Denne klassen kan brukes til å sikre at tilfeldige verdier er jevnt fordelt over det angitte området. For eksempel, hvis du genererer verdier mellom 1 og 10, og a 4 er generert, en annen 4 vil ikke bli generert før alle de andre tallene mellom 1 og 10 har også blitt generert.

Nå er det på tide å sette alt dette i praksis. Vi skal legge til to tilfeldige utdelinger til vårt spill. Åpne prosjektet ditt i Xcode og gå til GameScene.swift. Den første tilfeldige distribusjonen vi legger til er a GKGaussianDistribution. Senere legger vi også til en GKShuffledDistribution. Legg til følgende to egenskaper til GameScene klasse.

var initialSpawnDistribution = GKHaussianDistribution (randomSource: GKARC4RandomSource (), lavesteValue: 0, highestValue: 2) var respawnDistribution = GKShuffledDistribution (randomSource: GKARC4RandomSource (), lavesteValue: 0, highestValue: 2)

I denne utgaven lager vi to distribusjoner med en minimumsverdi på 0 og en maksimumsverdi på 2. For GKGaussianDistribution, gjennomsnittet og avviket beregnes automatisk i henhold til følgende ligninger:

  • mener = (maksimum - minimum) / 2
  • avvik = (maksimum - minimum) / 6

Gjennomsnittet av en Gauss-distribusjon er midtpunktet og avviket brukes til å beregne hvilken prosentandel av verdier som skal ligge innenfor et bestemt område fra gjennomsnittet. Prosentandelen av verdier innenfor et bestemt område er:

  • 68,27% innen 1 avvik fra gjennomsnittet
  • 95% innen 2 avvik fra gjennomsnittet
  • 100% innen 3 avvik fra gjennomsnittet

Dette betyr at ca 69% av de genererte verdiene skal være lik 1. Dette vil resultere i flere røde prikker i forhold til grønne og gule prikker. For å gjøre dette arbeidet må vi oppdatere initialSpawn metode.

I til sløyfe, erstatt følgende linje:

la respawnFactor = arc4random ()% 3 // Vil produsere en verdi mellom 0 og 2 (inkluderende)

med følgende:

la respawnFactor = self.initialSpawnDistribution.nextInt ()

De nextInt Metoden kan kalles på noe objekt som samsvarer med GKRandom protokoll og returnerer en tilfeldig verdi basert på kilden og eventuelt distribusjonen du bruker.

Bygg og kjør appen din, og flytt deg rundt kartet. Du bør se mye flere røde prikker i forhold til både grønne og gule prikker.

Den andre tilfeldige distribusjonen som vi vil bruke i spillet, kommer til å spille når vi håndterer regelsystembasert responjonsadferd.

2. Regelsystemer

GameplayKit regelsystemer brukes til å bedre organisere betinget logikk i spillet ditt og også introdusere fuzzy logikk. Ved å introdusere fuzzy logikk kan du få enheter i spillet ditt til å ta beslutninger basert på en rekke forskjellige regler og variabler, for eksempel spillhelse, nåværende fiendtlig telling og avstand til fienden. Dette kan være svært fordelaktig i forhold til enkle hvis og bytte om uttalelser.

Regelsystemer, representert av GKRuleSystem klasse, har tre viktige deler til dem:

  • Dagsorden. Dette er settet av regler som er lagt til i regelsystemet. Som standard evalueres disse reglene i den rekkefølgen de legges til i regelsystemet. Du kan endre salience eiendom av hvilken som helst regel som skal spesifiseres når du vil at den skal evalueres.
  • Statlig informasjon. De stat eiendom av a GKRuleSystem objekt er en ordbok, som du kan legge til noen data til, inkludert egendefinerte objekttyper. Disse dataene kan da brukes av regelsystemets regler når resultatet returneres.
  • fakta. Fakta innenfor et regelsystem representerer konklusjonene fra evalueringen av regler. Et faktum kan også representeres av en hvilken som helst objekttype i spillet ditt. Hvert faktum har også en tilsvarende medlemskapsklasse, som er en verdi mellom 0.0 og 1.0. Denne medlemsklassen representerer inkluderingen eller tilstedeværelsen av faktumet innenfor regelsystemet.

Regler seg selv, representert av GKRule klasse, har to hovedkomponenter:

  • predikat. Denne delen av regelen returnerer en boolesk verdi, som indikerer om kravene til regelen er oppfylt. En regels predikat kan opprettes ved å bruke en NSPredicate objekt eller, som vi vil gjøre i denne opplæringen, en blokk med kode.
  • Handling. Når regjeringens predikat returnerer ekte, det er handlingen utføres. Denne handlingen er en blokk med kode hvor du kan utføre logikk hvis regelens krav er oppfylt. Dette er her du generelt hevder (legg til) eller trekker inn (fjern) fakta i foreldre regelsystemet.

La oss se hvordan alt dette fungerer i praksis. For vårt regelsystem skal vi lage tre regler som ser på:

  • Avstanden fra gyte peker til spilleren. Hvis denne verdien er relativt liten, vil vi gjøre spillet mer sannsynlig å gyte røde fiender.
  • den nåværende noden teller av scenen. Hvis dette er for høyt, ønsker vi ikke at flere punkter blir lagt til scenen.
  • om en prikk allerede er tilstede på spissen. Hvis det ikke er det, vil vi fortsette å gyte en prikk her.

Først legger du til følgende eiendom i GameScene klasse:

var ruleSystem = GKRuleSystem ()

Deretter legger du til følgende kodebit til didMoveToView (_ :) metode:

la playerDistanceRule = GKRule (blockPredicate: (system: GKRuleSystem) -> Bool i hvis la verdi = system.state ["spawnPoint"] som? NSValue let point = value.CGPointValue () la xDistance = abs (point.x - self.playerNode.position.x) la yDistance = abs (point.y - self.playerNode.position.y) la totalDistance = sqrt ((xDistance * xDistance) + (yDistance * yDistance)) hvis totalDistance <= 200  return true  else  return false   else  return false  )  (system: GKRuleSystem) -> Gyldig i system.assertFact ("spawnEnemy") la nodeCountRule = GKRule (blockPredicate: (system: GKRuleSystem) -> Bool i if self.children.count <= 50  return true  else  return false  )  (system: GKRuleSystem) -> Gyldig i system.assertFact ("shouldSpawn", klasse: 0.5) la nodePresentRule = GKRule (blockPredicate: (system: GKRuleSystem) -> Bool i hvis la verdi = system.state ["spawnPoint"] som? NSValue hvor selv. nodesAtPoint (value.CGPointValue ()) telle == 0 return true else return false) (system: GKRuleSystem) -> Opphevet i la grade = system.gradeForFact ("shouldSpawn") system.assertFact shouldSpawn ", klasse: (klasse + 0,5)) self.ruleSystem.addRulesFromArray ([playerDistanceRule, nodeCountRule, nodePresentRule])

Med denne koden oppretter vi tre GKRule objekter og legg dem til regelsystemet. Reglene hevder et bestemt faktum innenfor deres handlingsblokk. Hvis du ikke gir en karakterverdi og bare ringe til assertFact (_ :) metode, som vi gjør med playerDistanceRule, faktum er gitt en standard karakter av 1.0.

Du vil legge merke til det for nodeCountRule vi hevder bare "ShouldSpawn" faktisk med en karakter på 0.5. De nodePresentRule så hevder det samme faktum og legger til en karakterverdi på 0.5. Dette er gjort slik at når vi sjekker faktum senere, en karakterverdi på 1.0 betyr at begge regler er oppfylt.

Du vil også se at begge playerDistanceRule og nodePresentRule få tilgang til "SpawnPoint" verdien av regelsystemet stat ordbok. Vi vil tildele denne verdien før du evaluerer regelsystemet.

Til slutt finner og erstatt respawn metode i GameScene klasse med følgende implementering:

func respawn () la endNode = GKGraphNode2D (punkt: float2 (x: 2048.0, y: 2048.0)) self.graph.connectNodeUsingObstacles (endNode) for punkt i self.spawnPoints self.ruleSystem.reset () self.ruleSystem.state ["spawnPoint"] = NSValue (CGPoint: punkt) self.ruleSystem.evaluate () hvis self.ruleSystem.gradeForFact ("shouldSpawn") == 1.0 var respawnFactor = self.respawnDistribution.nextInt () hvis self.ruleSystem.gradeForFact ("spawnEnemy") == 1.0 respawnFactor = self.initialSpawnDistribution.nextInt () var node: SKShapeNode? = null bryter respawnFactor case 0: node = PointsNode (circleOfRadius: 25) node! .physicsBody = SKPhysicsBody (circleOfRadius: 25) node! .fillColor = UIColor.greenColor () tilfelle 1: node = RedEnemyNode (circleOfRadius: 75) node! .physicsBody = SKPhysicsBody (circleOfRadius: 75) node! .fillColor = UIColor.redColor () tilfelle 2: node = YellowEnemyNode (circleOfRadius: 50) node! .physicsBody = SKPhysicsBody (circleOfRadius: 50) node! .fillColor = UIColor.yellowColor ) standard: break hvis la enheten = node? .valueForKey ("entity") som? GKEntity, la agent = node? .ValueForKey ("agent") som? GKAgent2D hvor respawnFactor! = 0 entity.addComponent (agent) agent.delegate = node som? ContactNode agent.position = float2 (x: Float (punkt.x), y: Float (point.y)) agents.append (agent) la startNode = GKGraphNode2D (punkt: agent.position) self.graph.connectNodeUsingObstacles (startNode) la pathNodes = self.graph.findPathFromNode (startNode, toNode: endNode) som! [GKGraphNode2D] hvis! PathNodes.isEmpty let path = GKPath (graphNodes: pathNodes, radius: 1.0) la følgePath = GKGoal (toFollowPath: path, maxPredictionTime: 1.0, forward: true) la stayOnPath = GKGoal (toStayOnPath: path, maxPredictionTime: 1.0) la oppførsel = GKBehavior (mål: [followPath, stayOnPath]) agent.behavior = oppførsel self.graph.removeNodes ([startNode]) agent.mass = 0.01 agent.maxSpeed ​​= 50 agent.maxAcceleration = 1000 node !. posisjon = punktnode! .strokeColor = UIColor.clearColor () node! .physicsBody! .contactTestBitMask = 1 self.addChild (node!) self.graph.removeNodes ([endNode])

Denne metoden vil bli kalt en gang hvert sekund og er svært lik den initialSpawn metode. Det er en rekke viktige forskjeller i til sløyfe skjønt.

  • Vi nullstiller regelsystemet først ved å ringe det tilbakestille metode. Dette må gjøres når et regelsystem evalueres sekventielt. Dette fjerner alle påståtte fakta og relaterte data for å sikre at ingen informasjon er igjen fra den forrige evalueringen som kan forstyrre neste.
  • Vi tildeler deretter gytepunktet til regelsystemet stat ordbok. Vi bruker en NSValue objekt, fordi CGPoint datatypen stemmer ikke overens med Swift's AnyObject protokoll og kan ikke tilordnes dette NSMutableDictionary eiendom.
  • Vi evaluerer regelsystemet ved å ringe det evaluere metode.
  • Vi henter deretter regelsystemets medlemsklasse for "ShouldSpawn" faktum. Hvis dette er lik 1, vi fortsetter med å respektere prikken.
  • Endelig sjekker vi regelsystemets karakter for "SpawnEnemy" faktum og, hvis lik til 1, bruk den normalt distribuerte tilfeldige generatoren for å lage vår spawnFactor.

Resten av respawn Metoden er den samme som initialSpawn metode. Bygg og kjør spillet en siste gang. Selv uten å flytte rundt, vil du se nye prikker gyte når de nødvendige forholdene er oppfylt.

Konklusjon

I denne serien på GameplayKit har du lært mye. La oss kort oppsummere hva vi har dekket.

  • Enheter og komponenter
  • Statlige maskiner
  • Agenter, mål og oppførsel
  • Stifinning
  • Tilfeldige verdi generatorer
  • Regelsystemer

GameplayKit er et viktig tillegg til iOS 9 og OS X El Capitan. Det eliminerer mye av kompleksiteten i spillutvikling. Jeg håper at denne serien har motivert deg til å eksperimentere mer med rammen og oppdage hva det er i stand til.

Som alltid, vær så snill å legge igjen dine kommentarer og tilbakemelding nedenfor.