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.
I denne tredje og siste opplæringen skal jeg lære deg om to andre funksjoner du kan bruke i dine egne spill:
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.
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 GKARC4RandomSource
, GKLinearCongruentialRandomSource
, 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:
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.
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:
salience
eiendom av hvilken som helst regel som skal spesifiseres når du vil at den skal evalueres.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.Regler seg selv, representert av GKRule
klasse, har to hovedkomponenter:
NSPredicate
objekt eller, som vi vil gjøre i denne opplæringen, en blokk med kode.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å:
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.
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.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.evaluere
metode."ShouldSpawn"
faktum. Hvis dette er lik 1, vi fortsetter med å respektere prikken."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.
I denne serien på GameplayKit har du lært mye. La oss kort oppsummere hva vi har dekket.
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.