Aceasta este a doua parte din O introducere în GameplayKit. Dacă nu ați trecut încă prin prima parte, vă recomand să citiți primul tutorial înainte de a continua cu acesta.
În acest tutorial, vă voi învăța despre alte două caracteristici ale cadrului GameplayKit pe care le puteți profita de:
Prin folosirea agenților, a obiectivelor și a comportamentelor, vom construi o inteligență artificială de bază (AI) în jocul pe care am început-o în prima parte a acestei serii. AI va permite punctelor inamice ale roșu și galben să vizeze și să se îndrepte spre punctul nostru de joc albastru. Vom implementa, de asemenea, trasarea pe calea spre a extinde această AI pentru a naviga în jurul obstacolelor.
Pentru acest tutorial, puteți utiliza copia proiectului realizat din prima parte a acestei serii sau puteți descărca o copie nouă a codului sursă de la GitHub.
În GameplayKit, agenții, obiectivele și comportamentele sunt folosite în combinație cu altele pentru a defini modul în care obiectele diferite se mișcă în relație cu ele în întreaga scenă. Pentru un singur obiect (sau SKShapeNode
în jocul nostru), începeți prin a crea un agent, reprezentat de GKAgent
clasă. Cu toate acestea, pentru jocurile 2D, cum ar fi ale noastre, trebuie să folosim betonul GKAgent2D
clasă.
GKAgent
clasa este o subclasă de GKComponent
. Acest lucru înseamnă că jocul dvs. trebuie să utilizeze o structură bazată pe entitate și pe componente pe măsură ce v-am arătat în primul tutorial din această serie.
Agenții reprezintă poziția, dimensiunea și viteza obiectului. Apoi adăugați a comportament, reprezentat de GKBehaviour
clasă, acestui agent. În cele din urmă, creați un set de goluri, reprezentat de GKGoal
clasă și adăugați-le la obiectul de comportament. Obiectivele pot fi folosite pentru a crea mai multe elemente diferite de joc, de exemplu:
Obiectul dvs. de comportament monitorizează și calculează toate obiectivele pe care le adăugați și apoi retransmite aceste date către agent. Să vedem cum funcționează acest lucru în practică.
Deschideți proiectul Xcode și navigați la PlayerNode.swift. Mai întâi trebuie să ne asigurăm PlayerNode
clasa este conformă cu GKAgentDelegate
protocol.
clasa PlayerNode: SKShapeNode, GKAgentDelegate ...
Apoi, adăugați următorul bloc de coduri la PlayerNode
clasă.
var agent = GKAgent2D () // MARK: Agent Delegate func agentWillUpdate (agent: GKAgent) dacă agent2D = Agent ca? GKAgent2D agent2D.position = float2 (float (position.x), float (position.y)) func agentDidUpdate (agent: GKAgent) dacă agent2D = agent ca? GKAgent2D auto.position = CGPoint (x: CGFloat (agent2D.position.x), y: CGFloat (agent2D.position.y))
Începem prin adăugarea unei proprietăți la PlayerNode
astfel încât să avem întotdeauna o referință la obiectul agentului curent al jucătorului. Apoi, implementăm cele două metode ale GKAgentDelegate
protocol. Prin implementarea acestor metode, ne asigurăm că punctul de jucător afișat pe ecran va reflecta întotdeauna schimbările pe care GameplayKit le face.
agentWillUpdate (_ :)
metoda este chemată chiar înainte ca GameplayKit să caute comportamentul și obiectivele acelui agent pentru a determina unde ar trebui să se miște. De asemenea, agentDidUpdate (_ :)
metoda se numește imediat după ce GameplayKit a finalizat acest proces.
Punerea în aplicare a acestor două metode asigură faptul că nodul pe care îl vedem pe ecran reflectă modificările efectuate de GameplayKit și că GameplayKit utilizează ultima poziție a nodului atunci când efectuează calculele sale.
În continuare, deschideți-vă ContactNode.swift și înlocuiți conținutul fișierului cu următoarea implementare:
import Import UIKit Import SpriteKit Clasă GameplayKit ContactNode: SKShapeNode, GKAgentDelegate var agent = GKAgent2D () // MARK: Agent Delegate func agentWillUpdate (agent: GKAgent) dacă agent2D = Agent ca? GKAgent2D agent2D.position = float2 (float (position.x), float (position.y)) func agentDidUpdate (agent: GKAgent) dacă agent2D = agent ca? GKAgent2D auto.position = CGPoint (x: CGFloat (agent2D.position.x), y: CGFloat (agent2D.position.y))
Prin implementarea GKAgentDelegate
protocol în ContactNode
, vom permite ca toate celelalte puncte din jocul nostru să fie actualizate cu GameplayKit, precum și dot dot player.
Acum este momentul să vă configurați comportamentele și obiectivele. Pentru a face acest lucru, trebuie să avem grijă de trei lucruri:
În primul rând, deschis GameScene.swift și, la sfârșitul anului didMoveToView (_ :)
adăugați următoarele două linii de cod:
playerNode.entity.addComponent (playerNode.agent) playerNode.agent.delegate = playerNode
Cu aceste două linii de cod, adăugăm agentul ca o componentă și setăm delegatul agentului să fie nodul în sine.
Apoi, înlocuiți punerea în aplicare a initialSpawn
cu următoarea implementare:
func initialSpawn () pentru punctul in self.spawnPoints let respawnFactor = arc4random ()% 3 // Va produce o valoare intre 0 si 2 (inclusiv) var node: SKShapeNode? = nod comutator respawnFactor caz 0: nod = noduri PointNode (circleOfRadius: 25) nod! .physicsBody = nod!! .fillColor = UIColor.greenColor () caz 1: node = RedEnemyNode (circleOfRadius: 75) nod! .physicsBody = nodul SKPhysicsBody (cercOfRadius: 75) nod! .fillColor = UIColor.redColor () caz 2: nod = ) default: break dacă entitatea = nodul? .valueForKey ("entitatea") ca? GKEntity, permite agent = node? .ValueForKey ("agent") ca? GKAgent2D unde respawnFactor! = 0 entity.addComponent (agent) agent.delegate = nod ca? ContactNode agent.position = float2 (x: Float (point.x), y: Float (point.y)) agents.append (agent) lasa comportament = GKBehavior (obiectiv: GKGoal (toSeekAgent: playerNode.agent) ) agent.behavior = comportament agent.mas = 0.01 agent.maxSpeed = 50 agent.maxAcceleration = 1000 nod! .position = nod punct! .strokeColor = UIColor.clearColor () nod! .physicsBody! .contactTestBitMask = 1 self.addChild (nod!)
Cel mai important cod pe care l-am adăugat este localizat în dacă
declarație care urmează intrerupator
afirmație. Să trecem prin această linie de cod:
agenţi
. Vom adăuga această proprietate la GameScene
clasă într-un moment.GKBehavior
obiect cu un singur GKGoal
pentru a viza agentul jucătorului curent. greutate
parametrul din acest inițializator este folosit pentru a determina ce obiective ar trebui să aibă prioritate față de ceilalți. De exemplu, imaginați-vă că aveți un obiectiv de a viza un anumit agent și un obiectiv de a vă îndepărta de alt agent, dar doriți ca preferința să fie preferată. În acest caz, puteți oferi obiectivului de direcționare o pondere de 1
și obiectivul îndepărtat o greutate de 0.5
. Acest comportament este apoi atribuit agentului nodului inamic.masa
, viteza maxima
, și maxAcceleration
proprietățile agentului. Acestea afectează cât de repede se pot deplasa obiectele și se pot întoarce. Simțiți-vă liber să jucați cu aceste valori și să vedeți cum afectează mișcarea punctelor inamice.Apoi, adăugați următoarele două proprietăți la GameScene
clasă:
var agenți: [GKAgent2D] = [] var ultimUpdateTime: CFTimeInterval = 0,0
agenţi
array va fi folosit pentru a păstra o referință la agenții inamici din scenă. lastUpdateTime
proprietatea va fi utilizată pentru a calcula timpul care a trecut de la ultima actualizare a scenei.
În cele din urmă, înlocuiți punerea în aplicare a directivei Actualizați(_:)
metodă a GameScene
clasa cu următoarea implementare:
override function update (actualTime: CFTimeInterval) / * Numit înainte ca fiecare cadru să fie redat * / auto.camera?.position = playerNode.position dacă self.lastUpdateTime == 0 lastUpdateTime = currentTime permite delta = actualTime - lastUpdateTime lastUpdateTime = currentTime playerNode.agent.updateWithDeltaTime (delta) pentru agent în agenți agent.updateWithDeltaTime (delta)
În Actualizați(_:)
, vom calcula timpul care a trecut de la ultima actualizare a scenei și apoi vom actualiza agenții cu acea valoare.
Construiți și rulați aplicația dvs. și începeți să vă mișcați în jurul scenei. Veți vedea că punctele inamice vor începe încet să vă îndrepte spre voi.
După cum puteți vedea, în timp ce punctele inamice se adresează jucătorului curent, nu navighează în jurul barierelor albe, ci încearcă să se miște prin ele. Să-i facem pe inamici un pic mai deștepți cu trasarea pe traseu.
Cu frameworkul GameplayKit, puteți adăuga complexe trasee în jocul dvs. prin combinarea corpurilor de fizică cu clasele și metodele GameplayKit. Pentru jocul nostru, îl vom înființa astfel încât punctele inamice să vizeze punctul jucătorului și în același timp să navigheze în jurul obstacolelor.
Pathfinding în GameplayKit începe cu crearea unui grafic a scenei tale. Acest grafic este o colecție de locații individuale, denumită și noduri, și legăturile dintre aceste locații. Aceste conexiuni definesc modul în care un anumit obiect se poate muta de la o locație la alta. Un grafic poate modela căile disponibile în scenă în unul din trei moduri:
GKObstacleGraph
clasa este folosită pentru grafic, GKPolygonObstacle
clasa pentru obstacole, și GKGraphNode2D
clasă pentru noduri (locații).GKGridGraph
clasa este folosită pentru grafic și pentru GKGridGraphNode
clasa pentru noduri.GKGraph
clasa este folosită pentru grafic și pentru GKGraphNode
clasa pentru noduri.Pentru că vrem ca punctul jucător în jocul nostru să navigheze în jurul barierelor albe, vom folosi GKObstacleGraph
pentru a crea un grafic al scenei noastre. Pentru a începe, înlocuiți spawnPoints
proprietate în GameScene
clasă cu următoarele:
Spuneți-ne păstrați spawnPoints = [CGPoint (x: 245, y: 3900), CGPoint (x: 700, y: 3500) x: 1200, y: 2450), CGPoint (x: 1200, y: 2950), CGPoint (x: 3100), CGPoint (x: 3000, y: 2400), CGPoint (x: 2048, y: 2400)!
spawnPoints
array conține câteva locații modificate ale spawn în scopul acestui tutorial. Acest lucru se datorează faptului că în prezent GameplayKit poate calcula doar căile între obiecte care sunt relativ apropiate unele de altele.
Datorită distanței mari prestabilite între punctele din acest joc, trebuie adăugate câteva puncte noi pentru a ilustra traseul de traseu. Rețineți că, de asemenea, declarăm a grafic
proprietate de tip GKObstacleGraph
să păstrăm o referință la graficul pe care îl vom crea.
Apoi, adăugați următoarele două linii de cod la începutul didMoveToView (_ :)
metodă:
lasa obstacole = SKNode.obstaclesFromNodePhysicsBodies (self.children) graph = GKObstacleGraph (obstacole: obstacole, bufferRadius: 0.0)
În primul rând, vom crea o serie de obstacole de la corpurile de fizică din scenă. Apoi, creăm obiectul grafic folosind aceste obstacole. bufferRadius
parametrul din acest inițializator poate fi folosit pentru a forța obiectele să nu ajungă la o anumită distanță de aceste obstacole. Aceste linii trebuie adăugate la începutul didMoveToView (_ :)
pentru că graficul pe care îl creăm este necesar de timp initialSpawn
se numește metoda.
În cele din urmă, înlocuiți initialSpawn
cu următoarea implementare:
func initialSpawn () permite endNode = GKGraphNode2D (punct: float2 (x: 2048.0, y: 2048.0)) self.graph.connectNodeUsingObstacles (endNode) pentru point in self.spawnPoints let respawnFactor = arc4random o valoare între 0 și 2 (inclusiv) var node: SKShapeNode? = nod comutator respawnFactor caz 0: nod = noduri PointNode (circleOfRadius: 25) nod! .physicsBody = nod!! .fillColor = UIColor.greenColor () caz 1: node = RedEnemyNode (circleOfRadius: 75) nod! .physicsBody = nodul SKPhysicsBody (cercOfRadius: 75) nod! .fillColor = UIColor.redColor () caz 2: nod = ) default: break dacă entitatea = nodul? .valueForKey ("entitatea") ca? GKEntity, permite agent = node? .ValueForKey ("agent") ca? GKAgent2D unde respawnFactor! = 0 entity.addComponent (agent) agent.delegate = nod ca? ContactNode agent.position = float2 (x: float (point.x), y: float (point.y)) agents.append (agent) / * let behavior = GKBehavior (goal: GKGoal (toSeekAgent: playerNode.agent) : 1.0) agent.behavior = comportament * / / *** BEGIN PATHFINDING *** / let startNode = GKGraphNode2D (punct: agent.position) self.graph.connectNodeUsingObstacles (startNode) let pathNodes = self.graph.findPathFromNode (startNode, toNode: endNode) ca! [GKGraphNode2D] dacă! PathNodes.isEmpty permite calea = GKPath (graphNodes: pathNodes, radius: 1.0) permite followPath = GKGoal (toFollowPath: path, maxPredictionTime: 1.0, forward: true) 1.0) permite comportament = GKBehavior (scopuri: [followPath, stayOnPath]) agent.behavior = comportament self.graph.removeNodes ([startNode]) / *** END PATHFINDING *** / agent.mass = 0.01 agent.maxSpeed = 50 agent.maxAcceleration = 1000 nod! .Poziție = punct nod! .StrokeColor = UIColor.clearColor () nod! .PhysicsBody! .ContactTestBitMask = 1 auto.addChild (nod!) Self.graph.removeNodes ([endNode])
Începem metoda prin crearea unui GKGraphNode2D
obiect cu coordonatele prestabilite ale jucătorului. Apoi, conectăm acest nod la grafic, astfel încât acesta să poată fi utilizat atunci când găsim căi.
Cele mai multe dintre initialSpawn
metoda rămâne neschimbată. Am adăugat câteva comentarii pentru a vă arăta unde este localizată prima porțiune a codului pathfinding dacă
afirmație. Să trecem prin acest pas pas cu pas:
GKGraphNode2D
instanță și conectați-o la grafic.findPathFromNode (_: toNode :)
pe graficul nostru.rază
parametrul funcționează similar cu bufferRadius
parametru înainte și definește cât de mult un obiect poate să se îndepărteze de calea creată.GKGoal
obiecte, una pentru urmarea căii și alta pentru a rămâne pe cale. maxPredictionTime
parametrul permite ca obiectivul să calculeze cât mai bine poate anticipa dacă ceva va întrerupe obiectul de la urmărirea / șederea pe acea cale.De asemenea, veți observa că eliminăm nodurile pe care le creăm din grafic după terminarea lor. Aceasta este o bună practică de urmat deoarece asigură faptul că nodurile pe care le-ați creat nu interferează niciodată cu alte calcule de calculare a traseului.
Construiți și rulați aplicația pentru ultima oară și veți vedea că două puncte se dezvoltă foarte aproape de dvs. și încep să se îndrepte spre dvs. Este posibil să trebuiască să rulați jocul de mai multe ori dacă ambele apar ca puncte verzi.
Important!
În acest tutorial, am folosit funcția de deplasare a gameplayKit pentru a permite punctelor inamice să vizeze punctul jucătorului în jurul obstacolelor. Rețineți că aceasta a fost doar pentru un exemplu practic de identificare a traseului.
Pentru un joc de producție real, ar fi mai bine să implementați această funcție combinând obiectivul de direcționare a jucătorului cu cel precedent în acest tutorial, cu un obiectiv de evitare a obstacolelor creat cu init (toAvoidObstacles: maxPredictionTime :)
metode convenționale, despre care puteți citi mai multe despre GKGoal
Referință la clasă.
În acest tutorial, v-am arătat cum puteți utiliza agenți, obiective și comportamente în jocurile care au o structură a entității-componente. În timp ce am creat doar trei goluri în acest tutorial, există multe altele disponibile pentru dvs., despre care puteți citi mai multe despre GKGoal
Referință la clasă.
De asemenea, v-am arătat cum să implementați o metodă de căutare avansată în joc, creând un grafic, un set de obstacole și obiective pentru a urma aceste căi.
După cum puteți vedea, există o mare cantitate de funcționalitate pusă la dispoziția dvs. prin intermediul gameplayKit-ului. În cea de-a treia și ultima parte a acestei serii, vă voi învăța despre generatoarele de valori aleatorii ale GameplayKit și cum să vă creați propriul sistem de reguli pentru a introduce o anumită logică fuzzy în jocul dvs..
Ca întotdeauna, vă rugăm să vă lăsați în continuare comentariile și comentariile.