Ceci est la troisième partie de Une introduction à GameplayKit. Si vous n'avez pas encore lu la première et la deuxième partie, je vous recommande de lire ces tutoriels avant de continuer avec celui-ci..
Dans ce troisième et dernier tutoriel, je vais vous expliquer deux autres fonctionnalités que vous pouvez utiliser dans vos propres jeux:
Dans ce didacticiel, nous allons d’abord utiliser l’un des générateurs de valeurs aléatoires de GameplayKit pour optimiser notre algorithme initial de génération d’ennemis. Nous allons ensuite mettre en œuvre un système de règles de base en combinaison avec une autre distribution aléatoire pour gérer le comportement de réapparition des ennemis.
Pour ce tutoriel, vous pouvez utiliser votre copie du projet terminé du deuxième tutoriel ou télécharger une nouvelle copie du code source à partir de GitHub..
Des valeurs aléatoires peuvent être générées dans GameplayKit à l’aide de toute classe conforme à la GKRandom
protocole. GameplayKit fournit cinq classes conformes à ce protocole. Ces classes contiennent trois variables aléatoires sources et deux au hasard les distributions. La principale différence entre les sources aléatoires et les distributions aléatoires est que les distributions utilisent une source aléatoire pour produire des valeurs dans une plage spécifique et peuvent manipuler la sortie des valeurs aléatoires de diverses autres manières..
Les classes susmentionnées sont fournies par le framework afin que vous puissiez trouver le bon équilibre entre performances et aléas pour votre jeu. Certains algorithmes générateurs de valeur aléatoires sont plus complexes que d’autres et ont donc un impact sur les performances..
Par exemple, si vous avez besoin d’un nombre aléatoire généré à chaque image (soixante fois par seconde), il est préférable d’utiliser l’un des algorithmes les plus rapides. En revanche, si vous générez rarement une valeur aléatoire, vous pouvez utiliser un algorithme plus complexe afin de produire de meilleurs résultats..
Les trois classes de sources aléatoires fournies par le framework GameplayKit sont GKARC4RandomSource
, GKLinearCongruentialRandomSource
, et GKMersenneTwisterRandomSource
.
GKARC4RandomSource
Cette classe utilise l'algorithme ARC4 et convient à la plupart des applications. Cet algorithme fonctionne en produisant une série de nombres aléatoires basés sur une graine. Vous pouvez initialiser un GKARC4RandomSource
avec une graine spécifique si vous devez reproduire un comportement aléatoire d’une autre partie de votre jeu. La graine d'une source existante peut être extraite de sa la graine
propriété en lecture seule.
GKLinearCongruentialRandomSource
Cette classe de source aléatoire utilise l'algorithme de base du générateur congruentiel linéaire. Cet algorithme est plus efficace et plus performant que l’algorithme ARC4, mais il génère également des valeurs moins aléatoires. Vous pouvez aller chercher un GKLinearCongruentialRandomSource
la graine de l'objet et créer une nouvelle source avec elle de la même manière qu'un GKARC4RandomSource
objet.
GKMersenneTwisterRandomSource
Cette classe utilise le Mersenne Twister algorithme et génère les résultats les plus aléatoires, mais il est également le moins efficace. Tout comme les deux autres classes sources aléatoires, vous pouvez récupérer un GKMersenneTwisterRandomSource
la graine de l'objet et l'utiliser pour créer une nouvelle source.
Les deux classes de distribution aléatoire dans GameplayKit sont GKGaussianDistribution
et GKShuffledDistribution
.
GKGaussianDistribution
Ce type de distribution garantit que les valeurs aléatoires générées suivent une distribution gaussienne, également appelée distribution normale. Cela signifie que la majorité des valeurs générées se situeront au milieu de la plage spécifiée..
Par exemple, si vous configurez un GKGaussianDistribution
objet avec une valeur minimale de 1, une valeur maximale de dix, et un écart type de 1, environ 69% des résultats serait soit 4, 5, ou 6. Je vais expliquer cette distribution plus en détail lorsque nous en ajouterons un à notre jeu plus tard dans ce tutoriel..
GKShuffledDistribution
Cette classe peut être utilisée pour s'assurer que les valeurs aléatoires sont uniformément réparties sur la plage spécifiée. Par exemple, si vous générez des valeurs entre 1 et dix, et un 4 est généré, un autre 4 ne sera pas généré avant que tous les autres nombres entre 1 et dix ont également été générés.
Il est maintenant temps de mettre tout cela en pratique. Nous allons ajouter deux distributions aléatoires à notre jeu. Ouvrez votre projet dans Xcode et allez à GameScene.swift. La première distribution aléatoire que nous ajouterons est un GKGaussianDistribution
. Plus tard, nous ajouterons également un GKShuffledDistribution
. Ajoutez les deux propriétés suivantes à la GameScene
classe.
var initialSpawnDistribution = GKGaussianDistribution (randomSource: GKARC4RandomSource (), vautValeur: 0, valeur la plus élevée: 2) var respawnDistribution = GKShuffledDistribution (randomSource: GKARC4RandomSource (), valeur la plus élevée: 2)
Dans cet extrait, nous créons deux distributions avec une valeur minimale de 0 et une valeur maximale de 2. Pour le GKGaussianDistribution
, la moyenne et l'écart sont automatiquement calculés selon les équations suivantes:
moyenne = (maximum - minimum) / 2
écart = (maximum - minimum) / 6
La moyenne d'une distribution gaussienne est son point médian et l'écart est utilisé pour calculer quel pourcentage de valeurs doit se situer dans une certaine plage par rapport à la moyenne. Le pourcentage de valeurs dans une certaine plage est:
Cela signifie qu'environ 69% des valeurs générées doivent être égales à 1. Cela se traduira par davantage de points rouges proportionnellement aux points verts et jaunes. Pour que cela fonctionne, nous devons mettre à jour le initialSpawn
méthode.
dans le pour
boucle, remplace la ligne suivante:
let respawnFactor = arc4random ()% 3 // produira une valeur comprise entre 0 et 2 (inclus)
avec ce qui suit:
let respawnFactor = self.initialSpawnDistribution.nextInt ()
le nextInt
méthode peut être appelée sur n’importe quel objet conforme à la GKRandom
protocole et renverra une valeur aléatoire en fonction de la source et, le cas échéant, de la distribution que vous utilisez.
Générez et exécutez votre application, puis déplacez-vous sur la carte. Vous devriez voir beaucoup plus de points rouges par rapport aux points verts et jaunes.
La deuxième distribution aléatoire que nous utiliserons dans le jeu entrera en jeu lors du traitement du comportement de réapparition basé sur un système de règles.
Les systèmes de règles GameplayKit sont utilisés pour mieux organiser la logique conditionnelle au sein de votre jeu et également pour introduire la logique floue. En introduisant une logique floue, vous pouvez obliger les entités de votre jeu à prendre des décisions en fonction de différentes règles et variables, telles que l'état de santé du joueur, le nombre d'ennemis actuels et la distance qui le sépare. Cela peut être très avantageux par rapport au simple si
et commutateur
des déclarations.
Systèmes de règles, représentés par le GKRuleSystem
classe, ont trois parties clés pour eux:
saillance
propriété de n'importe quelle règle pour spécifier quand vous voulez qu'elle soit évaluée.Etat
propriété d'un GKRuleSystem
objet est un dictionnaire auquel vous pouvez ajouter toutes les données, y compris les types d'objet personnalisé. Ces données peuvent ensuite être utilisées par les règles du système de règles lors du renvoi du résultat..Les règles elles-mêmes, représentées par le GKRule
classe, ont deux composantes principales:
NSPredicate
objet ou, comme nous le ferons dans ce tutoriel, un bloc de code.vrai
, c'est l'action est exécutée. Cette action est un bloc de code dans lequel vous pouvez exécuter toute logique si les exigences de la règle ont été satisfaites. C’est là que vous affirmez (ajoutez) ou retirez (supprimez) des faits dans le système de règles parent.Voyons comment tout cela fonctionne dans la pratique. Pour notre système de règles, nous allons créer trois règles qui prennent en compte:
Tout d'abord, ajoutez la propriété suivante à la GameScene
classe:
var ruleSystem = GKRuleSystem ()
Ensuite, ajoutez l’extrait de code suivant à la didMoveToView (_ :)
méthode:
let playerDistanceRule = GKRule (blockPredicate: (system: GKRuleSystem) -> Bool dans if let value = system.state ["spawnPoint"] en tant que? NSValue let point = valeur.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)) si totalDistance <= 200 return true else return false else return false ) (system: GKRuleSystem) -> Nul dans system.assertFact ("spawnEnemy") let nodeCountRule = GKRule (blockPredicate: (système: GKRuleSystem) -> Bool dans if self.children.count <= 50 return true else return false ) (system: GKRuleSystem) -> Nul dans system.assertFact ("shouldSpawn", grade: 0.5) let nodePresentRule = GKRule (blockPredicate: (system: GKRuleSystem) -> Bool dans if let let = system.state ["spawnPoint"] comme? NSValue where self. nodesAtPoint (value.CGPointValue ()). count == 0 return true else return false) ((system: GKRuleSystem) -> Nul dans let grade = system.gradeForFact ("shouldSpawn") system.assertFact (" shouldSpawn ", note: (note + 0.5)) self.ruleSystem.addRulesFromArray ([playerDistanceRule, nodeCountRule, nodePresentRule])
Avec ce code, nous créons trois GKRule
objets et les ajouter au système de règles. Les règles affirment un fait particulier dans leur bloc d’action. Si vous ne fournissez pas de valeur de note et appelez simplement le assertFact (_ :)
méthode, comme nous le faisons avec playerDistanceRule
, le fait est donné une note par défaut de 1,0.
Vous remarquerez que pour le nodeCountRule
nous affirmons seulement le "devrait Spawn"
fait avec une note de 0.5. le nodePresentRule
puis affirme ce même fait et ajoute une valeur de grade de 0.5. Ceci est fait pour que lorsque nous vérifions le fait plus tard, une valeur de note de 1,0 signifie que les deux règles ont été satisfaites.
Vous verrez également que les deux playerDistanceRule
et nodePresentRule
accéder au "spawnPoint"
valeur du système de règles Etat
dictionnaire. Nous attribuerons cette valeur avant d'évaluer le système de règles..
Enfin, recherchez et remplacez le renaître
méthode dans le GameScene
classe avec l'implémentation suivante:
func respawn () let endNode = GKGraphNode2D (point: float2 (x: 2048.0, y: 2048.0)) self.graph.connectNodeUsingObstacles (endNode) pour le point dans self.spawnPoints self.ruleSystem.reset () self.ruleSystem.state ["spawnPoint"] = NSValue (CGPoint: point) self.ruleSystem.evaluate () si self.ruleSystem.gradeForFact ("shouldSpawn") == 1.0 var respawnFactor = self.respawnDistribution.nextInt () si (.) si self.ruleSystem.gradeForFact ("spawnEnemy") == 1.0 respawnFactor = self.initialSpawnDistribution.nextInt () noeud var: SKShapeNode? = nil switch respawnFactor case 0: noeud = PointsNode (circleOfRadius: 25) noeud! !physicsBody = SKPhysicsBody (circleOfRadius: 25) noeud! .fillColor = UIColor.greenColor () cas 1: noeud = RedEnemyNode (circleOfRadius: 75)!! .physicsBody = SKPhysicsBody (circleOfRadius: 75) noeud! !fillColor = UIColor.redColor () cas 2: noeud = YellowEnemyNode (circleOfRadius: 50) noeud! .physicsBody = SKPhysicsBody (circleOfRadius: 50) noeud! .physicsBody = SKPhysicsBody (circleOfRadius: 50) noeud! ) default: break si let entity = node? .valueForKey ("entity") as? GKEntity, laissez agent = node? .ValueForKey ("agent") comme? GKAgent2D où respawnFactor! = 0 entity.addComponent (agent) agent.delegate = noeud comme? ContactNode agent.position = float2 (x: Float (point.x), y: Float (point.y)) agents.append (agent) let startNode = GKGraphNode2D (point: agent.position) self.graph.connectNodeUsingObstacles (startNode) let pathNodes = self.graph.findPathFromNode (startNode, toNode: endNode) en tant que! [GKGraphNode2D] if! PathNodes.isEmpty let path = GKPath (graphNodes: pathNodes, rayon: 1.0) let followPath = GKGoal (toFollowPath: path, maxPredictionTime: 1.0, forward: true) let stayOnPath = GKGoal (toStayOmPall): 1.0) let behavior = GKBehavior (objectifs: [followPath, stayOnPath]) agent.behavior = comportement self.graph.removeNodes ([startNode]) agent.mass = 0.01 agent.maxSpeed = 50 agent.maxAcceleration = 1000 noeud !. position = noeud du point! .strokeColor = noeud UIColor.clearColor ()! .physicsBody! .contactTestBitMask = 1 self.addChild (noeud!) self.graph.removeNodes ([endNode])
Cette méthode sera appelée une fois par seconde et est très similaire à la initialSpawn
méthode. Il existe un certain nombre de différences importantes dans la pour
boucle si.
réinitialiser
méthode. Cela doit être fait lorsqu'un système de règles est évalué de manière séquentielle. Cela supprime tous les faits affirmés et les données associées afin de garantir qu'il ne reste aucune information de la précédente évaluation susceptible d’interférer avec la prochaine..Etat
dictionnaire. Nous utilisons un NSValue
objet, parce que le CGPoint
le type de données n'est pas conforme à celui de Swift AnyObject
protocole et ne peut pas être attribué à cette NSMutableDictionary
propriété.évaluer
méthode."devrait Spawn"
fait. Si cela est égal à 1, nous continuons avec respawning le point."spawnEnemy"
fait et, si égal à 1, utiliser le générateur aléatoire normalement distribué pour créer notre spawnFactor
.Le reste de la renaître
la méthode est la même que la initialSpawn
méthode. Construisez et lancez votre jeu une dernière fois. Même sans vous déplacer, vous verrez de nouveaux points apparaître lorsque les conditions nécessaires seront remplies.
Dans cette série sur GameplayKit, vous avez beaucoup appris. Résumons brièvement ce que nous avons couvert.
GameplayKit est un ajout important à iOS 9 et à OS X El Capitan. Il élimine une grande partie des complexités du développement de jeux. J'espère que cette série vous a motivé à expérimenter davantage le framework et à découvrir ce dont il est capable.
Comme toujours, assurez-vous de laisser vos commentaires ci-dessous.