Dies ist der dritte Teil von Eine Einführung in GameplayKit. Wenn Sie den ersten und den zweiten Teil noch nicht durchgearbeitet haben, empfehle ich, zuerst diese Tutorials zu lesen, bevor Sie mit diesem fortfahren.
In diesem dritten und letzten Tutorial werde ich Ihnen zwei weitere Funktionen beibringen, die Sie in Ihren eigenen Spielen verwenden können:
In diesem Lernprogramm verwenden wir zunächst einen der Zufallsgeneratoren von GameplayKit, um den anfänglichen Algorithmus für den Feindlaich zu optimieren. Wir implementieren dann ein grundlegendes Regelsystem in Kombination mit einer anderen Zufallsverteilung, um mit dem Respawn-Verhalten von Feinden umzugehen.
Für dieses Tutorial können Sie Ihre Kopie des abgeschlossenen Projekts aus dem zweiten Tutorial verwenden oder eine neue Kopie des Quellcodes von GitHub herunterladen.
Zufällige Werte können in GameplayKit mithilfe einer beliebigen Klasse generiert werden, die der entspricht GKRandom
Protokoll. GameplayKit bietet fünf Klassen, die diesem Protokoll entsprechen. Diese Klassen enthalten drei zufällige Quellen und zwei zufällig Ausschüttungen. Der Hauptunterschied zwischen Zufallsquellen und Zufallsverteilungen besteht darin, dass Verteilungen eine Zufallsquelle verwenden, um Werte innerhalb eines bestimmten Bereichs zu erzeugen, und die Ausgabe des Zufallswerts auf verschiedene andere Arten beeinflussen kann.
Die oben genannten Klassen werden vom Framework bereitgestellt, damit Sie die richtige Balance zwischen Leistung und Zufälligkeit für Ihr Spiel finden können. Einige Algorithmen zur Erzeugung von Zufallswerten sind komplexer als andere und beeinflussen daher die Leistung.
Wenn Sie beispielsweise eine Zufallszahl benötigen, die für jeden Frame generiert wird (sechzig Mal pro Sekunde), ist es am besten, einen der schnelleren Algorithmen zu verwenden. Im Gegensatz dazu können Sie, wenn Sie nur selten einen Zufallswert generieren, einen komplexeren Algorithmus verwenden, um bessere Ergebnisse zu erzielen.
Die drei zufälligen Quellklassen, die vom GameplayKit-Framework bereitgestellt werden, sind GKARC4RandomSource
, GKLinearCongruentialRandomSource
, und GKMersenneTwisterRandomSource
.
GKARC4RandomSource
Diese Klasse verwendet den ARC4-Algorithmus und ist für die meisten Zwecke geeignet. Dieser Algorithmus erzeugt eine Reihe von Zufallszahlen, die auf einem Samen basieren. Sie können ein initialisieren GKARC4RandomSource
mit einem bestimmten Startwert, wenn Sie zufälliges Verhalten aus einem anderen Teil Ihres Spiels replizieren müssen. Der Samen einer vorhandenen Quelle kann von dessen Quelle abgerufen werden Samen
schreibgeschützte Eigenschaft.
GKLinearCongruentialRandomSource
Diese Zufallsquellklasse verwendet den grundlegenden Algorithmus des linearen Kongruenzgenerators. Dieser Algorithmus ist effizienter und bietet bessere Ergebnisse als der ARC4-Algorithmus, generiert jedoch auch weniger zufällige Werte. Sie können eine abholen GKLinearCongruentialRandomSource
Keim des Objekts und erstellen Sie eine neue Quelle damit wie ein GKARC4RandomSource
Objekt.
GKMersenneTwisterRandomSource
Diese Klasse verwendet die Mersenne Twister Algorithmus und erzeugt die meisten zufälligen Ergebnisse, aber es ist auch am wenigsten effizient. Genau wie die beiden anderen zufälligen Quellklassen können Sie eine GKMersenneTwisterRandomSource
Objekt-Seed und verwenden Sie es, um eine neue Quelle zu erstellen.
Die zwei zufälligen Verteilungsklassen in GameplayKit sind GKGaussianDistribution
und GKShuffledDistribution
.
GKGaussianDistribution
Dieser Verteilungstyp stellt sicher, dass die generierten Zufallswerte einer Gaußschen Verteilung folgen, die auch als Normalverteilung bezeichnet wird. Dies bedeutet, dass der Großteil der generierten Werte in der Mitte des von Ihnen angegebenen Bereichs liegt.
Zum Beispiel, wenn Sie a einrichten GKGaussianDistribution
Objekt mit einem Mindestwert von 1, ein maximaler Wert von 10, und eine Standardabweichung von 1, CA 69% der Ergebnisse wäre entweder 4, 5, oder 6. Ich werde diese Verteilung ausführlicher erklären, wenn wir später in diesem Tutorial eine neue hinzufügen.
GKShuffledDistribution
Diese Klasse kann verwendet werden, um sicherzustellen, dass Zufallswerte gleichmäßig über den angegebenen Bereich verteilt sind. Zum Beispiel, wenn Sie Werte zwischen generieren 1 und 10, und ein 4 wird eine andere generiert 4 wird erst generiert, wenn alle anderen Zahlen zwischen 1 und 10 wurden auch generiert.
Es ist jetzt an der Zeit, all dies in die Praxis umzusetzen. Wir werden unserem Spiel zwei zufällige Distributionen hinzufügen. Öffnen Sie Ihr Projekt in Xcode und gehen Sie zu GameScene.swift. Die erste zufällige Verteilung, die wir hinzufügen, ist a GKGaussianDistribution
. Später fügen wir auch eine GKShuffledDistribution
. Fügen Sie die folgenden zwei Eigenschaften hinzu GameScene
Klasse.
var initialSpawnDistribution = GKGaussianDistribution (randomSource: GKARC4RandomSource (), niedrigster Wert: 0, höchster Wert: 2) var respawnDistribution = GKShuffledDistribution (randomSource: GKARC4RandomSource (), niedrigsterWert: 0,
In diesem Snippet erstellen wir zwei Distributionen mit einem Mindestwert von 0 und einen maximalen Wert von 2. Für die GKGaussianDistribution
, Mittelwert und Abweichung werden automatisch nach folgenden Gleichungen berechnet:
Mittelwert = (Maximum - Minimum) / 2
Abweichung = (Maximum - Minimum) / 6
Der Mittelwert einer Gaußschen Verteilung ist der Mittelpunkt und die Abweichung wird verwendet, um zu berechnen, wie viel Prozent der Werte innerhalb eines bestimmten Bereichs vom Mittelwert liegen sollten. Der Prozentsatz der Werte innerhalb eines bestimmten Bereichs beträgt:
Dies bedeutet, dass ungefähr 69% der generierten Werte gleich 1 sein sollten. Dies führt zu mehr roten Punkten im Verhältnis zu grünen und gelben Punkten. Damit dies funktioniert, müssen wir das aktualisieren Anfangsspawn
Methode.
In dem zum
Schleife, ersetzen Sie die folgende Zeile:
let respawnFactor = arc4random ()% 3 // Erzeugt einen Wert zwischen 0 und 2 (einschließlich)
mit den folgenden:
Lassen Sie respawnFactor = self.initialSpawnDistribution.nextInt ()
Das nextInt
Die Methode kann für jedes Objekt aufgerufen werden, das dem entspricht GKRandom
Protokoll und gibt einen zufälligen Wert basierend auf der Quelle und ggf. der verwendeten Verteilung zurück.
Erstellen und starten Sie Ihre App und bewegen Sie sich auf der Karte. Sie sollten viel mehr rote Punkte im Vergleich zu grünen und gelben Punkten sehen.
Die zweite Zufallsverteilung, die wir im Spiel verwenden werden, wird beim Umgang mit dem auf Regeln basierenden Respawn-Verhalten zum Tragen kommen.
GameplayKit-Regelsysteme werden verwendet, um die bedingte Logik in Ihrem Spiel besser zu organisieren und eine Fuzzy-Logik einzuführen. Durch die Einführung der Fuzzy-Logik können Sie Entitäten innerhalb Ihres Spiels dazu bringen, Entscheidungen auf der Grundlage verschiedener Regeln und Variablen zu treffen, z. B. der Spielergesundheit, der aktuellen Feindzahl und der Entfernung zum Feind. Dies kann im Vergleich zu einfach sehr vorteilhaft sein ob
und Schalter
Aussagen.
Regelsysteme, vertreten durch GKRuleSystem
Klasse, haben drei Schlüsselteile:
salience
Eigenschaft einer Regel, die angegeben werden soll, wann sie ausgewertet werden soll.Zustand
Eigentum von a GKRuleSystem
object ist ein Wörterbuch, zu dem Sie beliebige Daten hinzufügen können, einschließlich benutzerdefinierter Objekttypen. Diese Daten können dann von den Regeln des Regelsystems verwendet werden, wenn das Ergebnis zurückgegeben wird.Regeln selbst, vertreten durch die GKRule
Klasse haben zwei Hauptkomponenten:
NSPredicate
object oder, wie in diesem Tutorial beschrieben, einen Codeblock.wahr
, Ihre Aktion wird ausgeführt. Diese Aktion ist ein Codeblock, in dem Sie eine beliebige Logik ausführen können, wenn die Anforderungen der Regel erfüllt sind. Hier können Sie im Allgemeinen Tatsachen innerhalb des übergeordneten Regelsystems geltend machen (hinzufügen) oder zurückziehen (entfernen).Mal sehen, wie das alles in der Praxis funktioniert. Für unser Regelsystem werden wir drei Regeln erstellen, die Folgendes berücksichtigen:
Fügen Sie zunächst die folgende Eigenschaft zum hinzu GameScene
Klasse:
var ruleSystem = GKRuleSystem ()
Fügen Sie als Nächstes das folgende Codeausschnitt hinzu didMoveToView (_ :)
Methode:
let playerDistanceRule = GKRule (blockPredicate: (system: GKRuleSystem) -> Bool in, wenn value = system.state ["spawnPoint"] als? NSValue let point = value.CGPointValue () let xDistance = abs (point.x - self.playerNode.position.x) let yDistance = abs (point.y - self.playerNode.position.y) let totalDistance = sqrt ((xDistance * xDistance) + (yDistance * yDistance)) wenn totalDistance <= 200 return true else return false else return false ) (system: GKRuleSystem) -> Void in system.assertFact ("spawnEnemy") Lasse nodeCountRule = GKRule (blockPredicate: (system: GKRuleSystem) -> Bool in if self.children.count <= 50 return true else return false ) (system: GKRuleSystem) -> Void in system.assertFact ("shouldSpawn", Bewertung: 0,5) nodePresentRule = GKRule (blockPredicate: (system: GKRuleSystem) -> Bool in if let value = system.state ["spawnPoint"] als? NSValue wo selbst. nodeAtPoint (value.CGPointValue ()). count == 0 return true else return false) ) (System: GKRuleSystem) -> Nicht gültig in grade Klasse = system.gradeForFact ("shouldSpawn") system.assertFact (" shouldSpawn ", Benotung: (Note + 0,5)) self.ruleSystem.addRulesFromArray ([playerDistanceRule, nodeCountRule, nodePresentRule])
Mit diesem Code erstellen wir drei GKRule
Objekte und fügen Sie sie dem Regelsystem hinzu. Die Regeln behaupten eine bestimmte Tatsache in ihrem Aktionsblock. Wenn Sie keinen Notenwert angeben, rufen Sie einfach die assertFact (_ :)
Methode, wie wir es mit der playerDistanceRule
, der Tatsache wird eine Standardnote von 1,0.
Sie werden das für das bemerken nodeCountRule
wir behaupten nur das "shouldSpawn"
Tatsache mit einer Note von 0,5. Das nodePresentRule
dann behauptet diese gleiche Tatsache und addiert einen Notenwert von 0,5. Dies geschieht so, dass, wenn wir die Tatsache später prüfen, ein Notenwert von 1,0 bedeutet, dass beide Regeln erfüllt wurden.
Sie werden auch sehen, dass beide playerDistanceRule
und nodePresentRule
Greife auf ... zu "Spawnpunkt"
Wert des Regelsystems Zustand
Wörterbuch. Wir werden diesen Wert vor der Auswertung des Regelsystems zuweisen.
Finden Sie schließlich das Respawn
Methode in der GameScene
Klasse mit der folgenden Implementierung:
func respawn () let endNode = GKGraphNode2D (Punkt: float2 (x: 2048.0, y: 2048.0)) self.graph.connectNodeUsingacles (endNode) für einen Punkt in self.spawnPoints self.ruleSystem.reset () self.ruleSystem.state ["spawnPoint"] = NSValue (CGPoint: point) 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-Knoten: SKShapeNode? = nil switch respawnFactor Fall 0: Knoten = PointsNode (CircleOfRadius: 25) Knoten! .physicsBody = SKPhysicsBody (CircleOfRadius: 25) Knoten! .fillColor = UIColor.greenColor () Fall 1: Knoten = RedEnemyNode (75) .physicsBody = SKPhysicsBody (circleOfRadius: 75) Knoten! .fillColor = UIColor.redColor (), Fall 2: Knoten = YellowEnemyNode (circleOfRadius: 50) Knoten! .physicsBody = SKPhysicsBody = SKPhysicsBody (50). ) default: break wenn Entität = Knoten? .valueForKey ("Entität") als? GKEntity, lassen Sie agent = node? .ValueForKey ("agent") als? GKAgent2D wo respawnFactor! = 0 entity.addComponent (agent) agent.delegate = Knoten wie? ContactNode agent.position = float2 (x: Float (point.x), y: Float (point.y)) agents.append (agent) let startNode = GKGraphNode2D (point: agent.position) self.graph.connectNodeUsingacles (startNode) let pathNodes = self.graph.findPathFromNode (startNode, toNode: endNode) als! [GKGraphNode2D] if! PathNodes.isEmpty let path = GKPath (graphNodes: pathNodes, radius: 1.0) let followPath = GKGoal (toFollowPath: path, maxPredictionTime: 1.0, forward: true) let stayOnPath =atKanal (toStayOnPath): 1.0) let Verhalten = GKBehavior (Ziele: [followPath, stayOnPath]) agent.behavior = Verhalten self.graph.removeNodes ([startNode]) agent.mass = 0.01 agent.maxSpeed = 50 agent.maxAcceleration = 1000 Knoten !. position = Punktknoten! .strokeColor = UIColor.clearColor () - Knoten! .physicsBody! .contactTestBitMask = 1 self.addChild (node!) self.graph.removeNodes ([endNode])
Diese Methode wird einmal pro Sekunde aufgerufen und ähnelt der Anfangsspawn
Methode. Es gibt eine Reihe wichtiger Unterschiede in der zum
Schleife aber.
zurücksetzen
Methode. Dies muss geschehen, wenn ein Regelsystem sequentiell ausgewertet wird. Dadurch werden alle behaupteten Fakten und zugehörigen Daten entfernt, um sicherzustellen, dass keine Informationen aus der vorherigen Auswertung übrig bleiben, die die nächste stören könnten.Zustand
Wörterbuch. Wir benutzen ein NSValue
Objekt, weil die CGPoint
Datentyp entspricht nicht dem von Swift AnyObject
Protokoll und kann diesem nicht zugeordnet werden NSMutableDictionary
Eigentum.bewerten
Methode."shouldSpawn"
Tatsache. Wenn das gleich ist 1, wir setzen den Punkt fort."spawnEnemy"
Tatsache und, wenn gleich 1, Verwenden Sie den normalverteilten Zufallsgenerator, um unseren zu erstellen SpawnFactor
.Der Rest des Respawn
Methode ist die gleiche wie die Anfangsspawn
Methode. Bauen Sie Ihr Spiel ein letztes Mal auf und führen Sie es aus. Auch ohne sich zu bewegen, werden neue Punkte angezeigt, wenn die erforderlichen Bedingungen erfüllt sind.
In dieser Serie von GameplayKit haben Sie viel gelernt. Fassen wir kurz zusammen, was wir behandelt haben.
GameplayKit ist eine wichtige Ergänzung zu iOS 9 und OS X El Capitan. Es beseitigt viele Komplexitäten der Spielentwicklung. Ich hoffe, dass diese Serie Sie dazu motiviert hat, mehr mit dem Rahmen zu experimentieren und herauszufinden, wozu er in der Lage ist.
Bitte hinterlassen Sie wie immer Ihre Kommentare und Rückmeldungen.