Een inleiding tot gameplayKit deel 3

Dit is het derde deel van Een inleiding tot GameplayKit. Als je het eerste deel en het tweede deel nog niet hebt doorgenomen, raad ik aan deze tutorials eerst te lezen voordat je verdergaat met deze.

Invoering

In deze derde en laatste zelfstudie ga ik je leren over twee andere functies die je in je eigen games kunt gebruiken:

  • willekeurige waarde generatoren
  • regelsystemen

In deze tutorial zullen we eerst een van de random value-generators van GameplayKit gebruiken om ons aanvankelijk vijandelijk spawning-algoritme te optimaliseren. We zullen dan een basisregelsysteem implementeren in combinatie met een andere willekeurige verdeling om het respawelgedrag van vijanden aan te pakken.

Voor deze zelfstudie kunt u uw exemplaar van het voltooide project uit de tweede zelfstudie gebruiken of een nieuwe kopie van de broncode downloaden van GitHub.

1. Willekeurige waarde-generatoren

Willekeurige waarden kunnen in GameplayKit worden gegenereerd door elke klasse te gebruiken die voldoet aan de GKRandom protocol. GameplayKit biedt vijf klassen die voldoen aan dit protocol. Deze klassen bevatten drie willekeurige bronnen en twee willekeurig distributies. Het belangrijkste verschil tussen willekeurige bronnen en willekeurige verdelingen is dat distributies een willekeurige bron gebruiken om waarden binnen een specifiek bereik te produceren en de uitvoer van willekeurige waarden op verschillende andere manieren kunnen manipuleren.

De bovengenoemde klassen worden geleverd door het framework, zodat je de juiste balans kunt vinden tussen prestaties en willekeur voor je game. Sommige algoritmen voor het genereren van willekeurige waarden zijn complexer dan andere en hebben dus een impact op de prestaties.

Als u bijvoorbeeld een willekeurig nummer nodig hebt dat elk frame heeft gegenereerd (zestig keer per seconde), kunt u het beste een van de snellere algoritmen gebruiken. Als u daarentegen maar zelden een willekeurige waarde genereert, kunt u een complexer algoritme gebruiken om betere resultaten te produceren.

De drie willekeurige bronklassen die door het GameplayKit-framework worden geboden, zijn GKARC4RandomSourceGKLinearCongruentialRandomSource, en GKMersenneTwisterRandomSource.

GKARC4RandomSource

Deze klasse maakt gebruik van het ARC4-algoritme en is geschikt voor de meeste doeleinden. Dit algoritme werkt door een reeks willekeurige getallen te produceren op basis van een seed. U kunt een a initialiseren GKARC4RandomSource met een specifieke seed als je willekeurig gedrag uit een ander deel van je game moet repliceren. Het zaad van een bestaande bron kan worden opgehaald zaad alleen-lezen-eigenschap.

GKLinearCongruentialRandomSource

Deze willekeurige bronklasse maakt gebruik van het lineaire basisalgoritme generatoralgoritme. Dit algoritme is efficiënter en presteert beter dan het ARC4-algoritme, maar genereert ook minder willekeurige waarden. Je kunt een GKLinearCongruentialRandomSource het zaad van het object en creëer er een nieuwe bron op dezelfde manier als een GKARC4RandomSource voorwerp.

GKMersenneTwisterRandomSource

Deze klasse gebruikt de Mersenne Twister algoritme en genereert de meest willekeurige resultaten, maar het is ook het minst efficiënt. Net als de andere twee willekeurige bronklassen, kunt u een GKMersenneTwisterRandomSource het zaad van het object en gebruik het om een ​​nieuwe bron te creëren.

De twee willekeurige distributieklassen in GameplayKit zijn GKGaussianDistribution en GKShuffledDistribution.

GKGaussianDistribution

Dit distributietype zorgt ervoor dat de gegenereerde willekeurige waarden een Gauss-verdeling volgen, ook wel een normale verdeling genoemd. Dit betekent dat het grootste deel van de gegenereerde waarden in het midden van het bereik valt dat u opgeeft.

Bijvoorbeeld als u een GKGaussianDistribution object met een minimumwaarde van 1, een maximale waarde van 10, en een standaardafwijking van 1, ongeveer 69% van de resultaten zou ook zijn 4, 5, of 6. Ik zal deze distributie nader toelichten wanneer we er later in deze tutorial een toevoegen aan onze game.

GKShuffledDistribution

Deze klasse kan worden gebruikt om ervoor te zorgen dat willekeurige waarden gelijkmatig worden verdeeld over het opgegeven bereik. Bijvoorbeeld als u waarden tussen genereert 1 en 10, en een 4 wordt gegenereerd, een andere 4 wordt niet gegenereerd tot alle andere getallen ertussen 1 en 10 zijn ook gegenereerd.

Het is nu tijd om dit in de praktijk te brengen. We zullen twee willekeurige distributies aan onze game toevoegen. Open uw project in Xcode en ga naar GameScene.swift. De eerste willekeurige verdeling die we zullen toevoegen is a GKGaussianDistribution. Later voegen we ook een toe GKShuffledDistribution. Voeg de volgende twee eigenschappen toe aan de GameScene klasse.

var initialSpawnDistribution = GKGaussianDistribution (randomSource: GKARC4RandomSource (), lowestValue: 0, highestValue: 2) var respawnDistribution = GKShuffledDistribution (randomSource: GKARC4RandomSource (), lowestValue: 0, highestValue: 2)

In dit fragment maken we twee distributies met een minimumwaarde van 0 en een maximale waarde van 2. Voor de GKGaussianDistribution, het gemiddelde en de afwijking worden automatisch berekend volgens de volgende vergelijkingen:

  • gemiddelde = (maximum - minimum) / 2
  • afwijking = (maximum - minimum) / 6

Het gemiddelde van een Gauss-verdeling is het middelpunt en de afwijking wordt gebruikt om te berekenen welk percentage van de waarden binnen een bepaald bereik van het gemiddelde zou moeten liggen. Het percentage waarden binnen een bepaald bereik is:

  • 68,27% binnen 1 afwijking van het gemiddelde
  • 95% binnen 2 afwijkingen van het gemiddelde
  • 100% binnen 3 afwijkingen van het gemiddelde

Dit betekent dat ongeveer 69% van de gegenereerde waarden gelijk moet zijn aan 1. Dit resulteert in meer rode stippen in verhouding tot groene en gele stippen. Om dit te laten werken, moeten we het initialSpawn methode.

In de voor loop, vervang de volgende regel:

let respawnFactor = arc4willekeurig ()% 3 // zal een waarde tussen 0 en 2 (inclusief) produceren

met het volgende:

laat respawnFactor = self.initialSpawnDistribution.nextInt ()

De nextInt methode kan worden aangeroepen op elk object dat voldoet aan de GKRandom protocol en retourneert een willekeurige waarde op basis van de bron en, indien van toepassing, de verdeling die u gebruikt.

Bouw en voer uw app uit en ga op de kaart. Je zou veel meer rode stippen moeten zien in vergelijking met zowel groene als gele stippen.

De tweede willekeurige verdeling die we in de game zullen gebruiken, zal een rol spelen bij het omgaan met het op regelsysteem gebaseerde respawn-gedrag.

2. Rule Systems

GameplayKit-regelsystemen worden gebruikt om voorwaardelijke logica beter in uw spel te organiseren en introduceren ook fuzzy-logica. Door fuzzy logic te introduceren, kun je entiteiten in je spel beslissingen laten nemen op basis van een reeks verschillende regels en variabelen, zoals de gezondheid van spelers, het aantal huidige vijanden en de afstand tot de vijand. Dit kan zeer voordelig zijn in vergelijking met eenvoudig als en schakelaar statements.

Regel systemen, vertegenwoordigd door de GKRuleSystem klasse, hebben drie belangrijke onderdelen:

  • Agenda. Dit zijn de regels die aan het regelsysteem zijn toegevoegd. Standaard worden deze regels geëvalueerd in de volgorde waarin ze aan het regelsysteem zijn toegevoegd. U kunt de opvallendheid eigenschap van een regel om aan te geven wanneer u wilt dat deze wordt geëvalueerd.
  • Staatsinformatie. De staat eigendom van een GKRuleSystem object is een woordenboek waaraan u gegevens kunt toevoegen, inclusief aangepaste objecttypen. Deze gegevens kunnen vervolgens worden gebruikt door de regels van het regelsysteem bij het retourneren van het resultaat.
  • feiten. Feiten binnen een regelsysteem vertegenwoordigen de conclusies getrokken uit de evaluatie van regels. Een feit kan ook worden vertegenwoordigd door elk objecttype in je spel. Elk feit heeft ook een overeenkomstige lidmaatschapscijfer, wat een waarde tussen is 0.0 en 1.0. Dit lidmaatschapscijfer vertegenwoordigt de opname of aanwezigheid van het feit binnen het regelsysteem.

Regels zelf, vertegenwoordigd door de GKRule klasse, hebben twee belangrijke componenten:

  • gezegde. Dit deel van de regel retourneert een Booleaanse waarde, waarmee wordt aangegeven of aan de vereisten van de regel is voldaan. Het predicaat van een regel kan worden gemaakt met behulp van een NSPredicate object of, zoals we in deze tutorial zullen doen, een codeblok.
  • Actie. Wanneer het predicaat van de regel terugkeert waar, het is actie wordt uitgevoerd. Deze actie is een codeblok waarin u logica kunt uitvoeren als aan de vereisten van de regel is voldaan. Dit is waar je over het algemeen feiten beweert (toevoegt) of intrekt (verwijdert) binnen het bovenliggende regelsysteem.

Laten we eens kijken hoe dit in de praktijk allemaal werkt. Voor ons regelsysteem gaan we drie regels maken die kijken naar:

  • de afstand van het spawn-punt tot de speler. Als deze waarde relatief klein is, zorgen we ervoor dat het spel meer rode vijanden oplevert.
  • de huidige knooppuntentelling van de scène. Als dit te hoog is, willen we niet dat er nog meer punten aan de scène worden toegevoegd.
  • of een stip al dan niet aanwezig is op het spawn-punt. Als dat niet het geval is, willen we hier een punt spawnen.

Voeg eerst de volgende eigenschap toe aan de GameScene klasse:

var ruleSystem = GKRuleSystem ()

Voeg vervolgens het volgende codefragment toe aan de didMoveToView (_ :) methode:

laat playerDistanceRule = GKRule (blockPredicate: (system: GKRuleSystem) -> Bool in if let value = system.state ["spawnPoint"] als? NSValue let point = value.CGPointValue () laat xDistance = abs (point.x - self.playerNode.position.x) laat yDistance = abs (point.y - self.playerNode.position.y) laat totalDistance = sqrt ((xDistance * xDistance) + (yDistance * yDistance)) als totalDistance <= 200  return true  else  return false   else  return false  )  (system: GKRuleSystem) -> Void in system.assertFact ("spawnEnemy") laat nodeCountRule = GKRule (blockPredicate: (system: GKRuleSystem) -> Bool in if self.children.count <= 50  return true  else  return false  )  (system: GKRuleSystem) -> Void in system.assertFact ("shouldSpawn", cijfer: 0.5) laat nodePresentRule = GKRule (blockPredicate: (system: GKRuleSystem) -> Bool in if let value = system.state ["spawnPoint"] as? NSValue where self. nodesAtPoint (value.CGPointValue ()). count == 0 return true else return false) (system: GKRuleSystem) -> Void in let grade = system.gradeForFact ("shouldSpawn") system.assertFact (" shouldSpawn ", cijfer: (cijfer + 0.5)) self.ruleSystem.addRulesFromArray ([playerDistanceRule, nodeCountRule, nodePresentRule])

Met deze code maken we er drie GKRule objecten en voeg ze toe aan het regelsysteem. De regels bevestigen een bepaald feit binnen hun actieblok. Als u geen waarde opgeeft en gewoon belt assertFact (_ :) methode, zoals we doen met de playerDistanceRule, het feit krijgt een standaardwaarde van 1.0.

Dat merk je voor de nodeCountRule we beweren alleen het "ShouldSpawn" feit met een cijfer van 0.5. De nodePresentRule verklaart vervolgens hetzelfde feit en voegt een cijferwaarde toe van 0.5. Dit wordt gedaan zodat, wanneer we het feit later controleren, een beoordelingswaarde van 1.0 betekent dat aan beide regels is voldaan.

Je zult ook zien dat zowel de playerDistanceRule en nodePresentRule Toegang krijgen tot "Spawnpoint" waarde van de regelsystemen staat woordenboek. We zullen deze waarde toewijzen voordat het regelsysteem wordt geëvalueerd.

Tot slot, zoek en vervang de respawn methode in de GameScene klasse met de volgende implementatie:

func respawn () let endNode = GKGraphNode2D (punt: float2 (x: 2048.0, y: 2048.0)) self.graph.connectNodeUsingObstacles (endNode) voor punt in self.spawnPoints self.ruleSystem.reset () self.ruleSystem.state ["spawnPoint"] = NSValue (CGPoint: punt) self.ruleSystem.evaluate () if self.ruleSystem.gradeForFact ("shouldSpawn") == 1.0 var respawnFactor = self.respawnDistribution.nextInt () if self.ruleSystem.gradeForFact ("spawnEnemy") == 1.0 respawnFactor = self.initialSpawnDistribution.nextInt () var node: SKShapeNode? = nil switch respawnFactor case 0: node = PointsNode (circleOfRadius: 25) node! .physicsBody = SKPhysicsBody (circleOfRadius: 25) node! .fillColor = UIColor.greenColor () case 1: node = RedEnemyNode (circleOfRadius: 75) node! .physicsBody = SKPhysicsBody (circleOfRadius: 75) node! .fillColor = UIColor.redColor () case 2: node = YellowEnemyNode (circleOfRadius: 50) node! .physicsBody = SKPhysicsBody (circleOfRadius: 50) node! .fillColor = UIColor.yellowColor ( ) standaard: onderbreken als entiteit = knooppunt? .valueForKey ("entiteit") als? GKEntity, let agent = node? .ValueForKey ("agent") as? GKAgent2D waarbij respawnFactor! = 0 entity.addComponent (agent) agent.delegate = node als? ContactNode agent.position = float2 (x: Float (point.x), y: Float (point.y)) agents.append (agent) laat startNode = GKGraphNode2D (point: agent.position) self.graph.connectNodeUsingObstacles (startNode) laat pathNodes = self.graph.findPathFromNode (startNode, toNode: endNode) as! [GKGraphNode2D] if! PathNodes.isEmpty let path = GKPath (graphNodes: pathNodes, radius: 1.0) let followPath = GKGoal (toFollowPath: path, maxPredictionTime: 1.0, forward: true) let stayOnPath = GKGoal (toStayOnPath: path, maxPredictionTime: 1.0) laat gedrag = GKBehavior (doelen: [followPath, stayOnPath]) agent.behavior = behaviour self.graph.removeNodes ([startNode]) agent.mass = 0.01 agent.maxSpeed ​​= 50 agent.maxAcceleration = 1000 knooppunt !. position = point node! .strokeColor = UIColor.clearColor () node! .physicsBody! .contactTestBitMask = 1 self.addChild (node!) self.graph.removeNodes ([endNode])

Deze methode wordt eenmaal per seconde aangeroepen en lijkt erg op de initialSpawn methode. Er zijn een aantal belangrijke verschillen in de voor loop echter.

  • We stellen eerst het regelsysteem opnieuw in door het te bellen reset methode. Dit moet worden gedaan wanneer een regelsysteem opeenvolgend wordt geëvalueerd. Hiermee worden alle beweerde feiten en gerelateerde gegevens verwijderd om ervoor te zorgen dat er geen informatie overblijft van de vorige evaluatie die van invloed kan zijn op de volgende.
  • Vervolgens wijzen we het spawn-punt toe aan de regelsystemen staat woordenboek. We gebruiken een NSValue object, omdat de CGPoint datatype komt niet overeen met dat van Swift AnyObject protocol en kan hier niet aan worden toegewezen NSMutableDictionary eigendom.
  • We evalueren het regelsysteem door het te bellen schatten methode.
  • Vervolgens halen we de lidmaatschapsrang van het regelsysteem voor de "ShouldSpawn" feit. Als dit gelijk is aan 1, we gaan verder met de respawnen van de stip.
  • Ten slotte controleren we de rang van het regelsysteem voor de "SpawnEnemy" feit en, indien gelijk aan 1, gebruik de normaal verdeelde willekeurige generator om onze te maken spawnFactor.

De rest van de respawn methode is hetzelfde als de initialSpawn methode. Bouw en run je spel nog een keer. Zelfs zonder te bewegen, zie je nieuwe punten spawnen als aan de noodzakelijke voorwaarden wordt voldaan.

Conclusie

In deze serie over GameplayKit heb je veel geleerd. Laten we kort samenvatten wat we hebben behandeld.

  • Entiteiten en componenten
  • Staatsmachines
  • Agenten, doelen en gedrag
  • Padvinden
  • Generatoren met willekeurige waarde
  • Regel systemen

GameplayKit is een belangrijke toevoeging aan iOS 9 en OS X El Capitan. Het elimineert veel van de complexiteit van game-ontwikkeling. Ik hoop dat deze serie je gemotiveerd heeft om meer met het raamwerk te experimenteren en te ontdekken waartoe het in staat is.

Laat zoals altijd uw opmerkingen en feedback hieronder achter.