Naast alle nieuwe functies en frameworks in iOS 9 en OS X El Capitan, met de releases van dit jaar creëerde Apple ook een geheel nieuw framework voor game-ontwikkelaars, GameplayKit. Met bestaande grafische API's (SpriteKit, SceneKit en Metal) waardoor het gemakkelijk is om fantastische games op iOS en OS X te maken, heeft Apple nu GameplayKit uitgebracht om het gemakkelijk te maken om games te maken die spelen goed. Dit nieuwe framework bevat vele klassen en functionaliteiten die kunnen worden gebruikt om eenvoudig complexe logica aan uw games toe te voegen.
In deze eerste tutorial zal ik je twee belangrijke aspecten van het GameplayKt-framework leren:
Voor deze zelfstudie moet je actief zijn Xcode 7 op OS X Yosemite of later. Hoewel dit niet verplicht is, is het aanbevolen dat u een fysiek apparaat gebruikt iOS 9 omdat je veel betere prestaties zult krijgen bij het testen van de op SpriteKit gebaseerde game die in deze tutorial wordt gebruikt.
U moet eerst het startersproject voor deze serie tutorials downloaden van GitHub. Zodra je dit hebt gedaan, open je het project in Xcode en voer je het uit op de iOS Simulator of op je apparaat.
Je zult zien dat het een heel basisspel is waarin je een blauwe stip bestuurt en door de kaart navigeert. Wanneer je botst met een rode stip, verlies je twee punten. Wanneer je botst met een groene stip, krijg je één punt. Wanneer je botst met een gele stip, wordt je eigen punt een paar seconden bevroren.
In deze fase is het een erg basisspel, maar in de loop van deze tutorialserie, en met de hulp van GameplayKit, gaan we veel meer functionaliteit en gameplay-elementen toevoegen.
Het eerste hoofdaspect van het nieuwe GameplayKit-framework is een code-structurerend concept op basis van entiteiten en componenten. Het werkt door jou, de ontwikkelaar, toe te staan gemeenschappelijke code te schrijven die door veel verschillende objecttypen in je spel wordt gebruikt, terwijl het goed georganiseerd en beheersbaar blijft. Het concept van entiteiten en componenten is bedoeld om de op gemeenschappelijke overerving gebaseerde benadering om gemeenschappelijke functionaliteit tussen objecttypen te delen te elimineren. De eenvoudigste manier om dit concept te begrijpen, is met enkele voorbeelden, dus stel je het volgende scenario voor:
Je bouwt een torenverdedigingsspel met drie hoofdtypen torens, Brand, Ijs, en Genezen. De drie soorten torens zouden enkele gemeenschappelijke gegevens delen, zoals gezondheid, grootte en sterkte. Je Vuur en Ijstorens moeten inkomende vijanden kunnen aanvallen om te schieten terwijl je Healtoren dat niet doet. Het enige dat uw Healtoren hoeft te doen, is uw andere torens binnen een bepaalde straal repareren als ze schade oplopen.
Laten we, met dit basisspelmodel in gedachten, eens kijken hoe uw code zou kunnen worden georganiseerd met behulp van een erfenis structuur:
Toren
klasse met gemeenschappelijke gegevens zoals gezondheid, grootte en sterkte.Firetower
, IceTower
, en HealTower
klassen die zouden erven van de Toren
klasse.HealTower
klas, je hebt de logica die verantwoordelijk is voor het helen van je andere torens binnen een bepaalde straal.Tot nu toe is deze structuur in orde, maar er doet zich nu een probleem voor wanneer je de targetingvaardigheden van de Fire and Ice Towers moet implementeren. Kopieer en plak dezelfde code in beide Firetower
en IceTower
klassen? Als u wijzigingen wilt aanbrengen, moet u uw code op meer dan één plaats wijzigen, wat vervelend en foutgevoelig is. En wat gebeurt er als u een nieuw towertype wilt toevoegen dat ook deze targetingfunctionaliteit nodig heeft? Kopieer en plak het voor de derde keer?
De beste manier lijkt te zijn om deze targetinglogica in de ouder te plaatsen Toren
klasse. Dit zou u toelaten om slechts één exemplaar van de code te hebben die slechts op één plaats hoeft te worden bewerkt. Als u deze code hier echter toevoegt, wordt de Toren
klasse een stuk groter en ingewikkelder dan het moet zijn wanneer niet alle subklassen die functionaliteit nodig hebben. Als u ook meer gedeelde functionaliteit tussen uw torentypes wilt toevoegen, uw Toren
klas zou geleidelijk groter en groter worden, wat het moeilijk zou maken om mee te werken.
Zoals je kunt zien, is het mogelijk om een gamemodel te maken op basis van erfenis, het kan heel snel en gemakkelijk ongeorganiseerd en moeilijk te beheren worden.
Laten we nu kijken hoe ditzelfde gamemodel kan worden gestructureerd met behulp van entiteiten en componenten:
Firetower
, IceTower
, en HealTower
entiteiten. Er kunnen meer entiteiten worden gemaakt voor meer torentypes die u later wilt toevoegen.BasicTower
component dat gezondheid, grootte, sterkte, enz. zou bevatten.Genezing
bestanddeel.Gericht op
component bevat de code die nodig is om binnenkomende vijanden te targeten.Met GameplayKit en deze structuur zou je dan een uniek entiteittype hebben voor elk type toren in je spel. Aan elke individuele entiteit kunt u de gewenste componenten toevoegen die u wilt. Bijvoorbeeld:
Firetower
en IceTower
entiteiten zouden elk een BasicTower
en Gericht op
component gekoppeld.HealTower
entiteit zou zowel een BasicTower
en een Genezing
bestanddeel.Zoals je kunt zien, is je gamemodel nu een stuk eenvoudiger en veelzijdiger door een op entiteiten en componenten gebaseerde structuur te gebruiken. Uw targetinglogica hoeft maar één keer te worden geschreven en linkt alleen naar de entiteiten die daarvoor nodig zijn. Op dezelfde manier kunnen uw basistorendata nog eenvoudig worden gedeeld tussen al uw torens zonder al uw andere algemene functies op te bouwen.
Een ander groot voordeel van deze entiteit- en componentgebaseerde structuur is dat componenten kunnen worden toegevoegd aan en verwijderd uit entiteiten wanneer u maar wilt. Als u bijvoorbeeld wilt dat uw heeltorens onder bepaalde omstandigheden worden uitgeschakeld, kunt u eenvoudig de Genezing
component van uw entiteit totdat aan de juiste voorwaarden is voldaan. Evenzo, als je wilde dat een van je Vuurtorens een tijdelijk genezend vermogen zou krijgen, zou je er gewoon een kunnen toevoegen Genezing
component voor uw Firetower
entiteit voor een specifieke hoeveelheid tijd.
Nu u vertrouwd bent met het concept van een entiteit- en componentgebaseerde gamemodelstructuur, laten we er een maken binnen ons eigen spel. In Xcode's Bestand Inspector, vind de entiteiten map binnen uw project. Voor het gemak zijn er al drie entiteitsklassen voor u, maar u gaat nu vanuit het niets een nieuwe entiteit maken.
Kiezen Bestand> Nieuw> Bestand ... of druk op Command-N om een nieuwe klasse te maken. Zorg ervoor dat u de Cocoa Touch Class sjabloon van de iOS> Bron sectie. Geef de klas een naam Speler en maak er een subklasse van GKEntity
.
U zult zien dat Xcode onmiddellijk na het openen van uw nieuwe bestand een fout zal vertonen. Om dit probleem op te lossen, voegt u de volgende importinstructie toe onder het bestaande importeer UIKit
uitspraak:
import GameplayKit
Ga terug naar PlayerNode.swift en voeg de volgende eigenschap toe aan de PlayerNode
klasse:
var entity = Player ()
Navigeer vervolgens naar de Components map in uw Xcode-project en maak een nieuwe klasse op dezelfde manier als voorheen. Geef deze klas een naam FlashingComponent en maak er een subklasse van GKComponent
zoals hieronder getoond.
De component die je zojuist hebt gemaakt, gaat het visuele knipperen van onze blauwe stip verwerken wanneer deze wordt geraakt door een rode stip en zich in een onkwetsbare staat bevindt. Vervang de inhoud van FlashingComponent.swift met het volgende:
import UIKit import SpriteKit import GameplayKit class FlashingComponent: GKComponent var nodeToFlash: SKNode! func startFlashing () let fadeAction = SKAction.sequence ([SKAction.fadeOutWithDuration (0,75), SKAction.fadeInWithDuration (0,75)]) nodeToFlash.runAction (SKAction.repeatActionForever (fadeAction), withKey: "flash") deinit nodeToFlash. removeActionForKey ("flash")
De implementatie houdt eenvoudig een verwijzing naar een SKNode
object en herhaalt fade-in en vervaag acties achter elkaar zolang het onderdeel actief is.
Ga terug naar GameScene.swift en voeg de volgende code ergens toe binnen de didMoveToView (_ :)
methode:
// Component toevoegen laten flash = FlashingComponent () flash.nodeToFlash = playerNode flash.startFlashing () playerNode.entity.addComponent (flash)
We creëren een FlashingComponent
object en stel het in om zijn knipperingen op de punt van de speler uit te voeren. De laatste regel voegt vervolgens het onderdeel toe aan de entiteit om het actief te houden en uit te voeren.
Bouw en voer uw app uit. Je zult nu zien dat je blauwe stip langzaam herhaaldelijk in en uit gaat.
Verwijder de code die u zojuist hebt toegevoegd van de. Voordat u verdergaat didMoveToView (_ :)
methode. Later voeg je deze code weer toe, maar alleen als je blauwe stip in zijn onkwetsbare staat komt.
In GameplayKit bieden state-machines een manier om gemakkelijk taken te identificeren en uit te voeren op basis van de huidige staat van een bepaald object. Op basis van het eerdere voorbeeld van een torenverdediging kunnen enkele mogelijke toestanden voor elke toren zijn opgenomen Actief
, invalide
, en Vernietigd
. Een groot voordeel van state-machines is dat u kunt specificeren naar welke staten een andere staat kan gaan. Met de drie voorbeeldstaten die hierboven zijn genoemd, kunt u met een toestandsmachine de toestandsmachine zo instellen dat:
invalide
wanneer Actief
en vice versaVernietigd
wanneer een van beide Actief
of invalide
Actief
of invalide
als het eenmaal is geweest Vernietigd
In de game voor deze zelfstudie houden we het heel simpel en hebben we alleen een normaal en onkwetsbaar staat.
In je project Staat Machine map, maak twee nieuwe klassen. Geef ze een naam Normale staat
en InvulnerableState
respectievelijk, met beide zijnde een subklasse van de GKState
klasse.
Vervang de inhoud van NormalState.swift met het volgende:
import UIKit import SpriteKit import GameplayKit class NormalState: GKState var node: PlayerNode init (withNode: PlayerNode) node = withNode overschrijf func isValidNextState (stateClass: AnyClass) -> Bool switch stateClass case is InvulnerableState.Type: return true default : return false overschrijven func didEnterWithPreviousState (previousState: GKState?) if let _ = previousState as? InvulnerableState node.entity.removeComponentForClass (FlashingComponent) node.runAction (SKAction.fadeInWithDuration (0.5))
De Normale staat
klasse bevat het volgende:
isValidNextState (_ :)
methode. De implementatie van deze methode retourneert een Booleaanse waarde, die aangeeft of de huidige statusklasse kan worden verplaatst naar de toestandsklasse die wordt geboden door de parameter method.didEnterWithPreviousState (_ :)
callback-methode. Bij de implementatie van de methode controleren we of de vorige staat de InvulnerableState
status en, indien waar, de knipperende component uit de entiteit van de speler verwijderen.Nu open InvulnerableState.swift en vervang de inhoud ervan door het volgende:
import UIKit import GameplayKit class InvulnerableState: GKState var node: PlayerNode init (withNode: PlayerNode) node = withNode overschrijf func isValidNextState (stateClass: AnyClass) -> Bool switch stateClass case is NormalState.Type: return true default: return false overschrijven func didEnterWithPreviousState (previousState: GKState?) if let _ = previousState as? NormalState // Component toevoegen laat flashen = FlashingComponent () flash.nodeToFlash = knooppunt flash.startFlashing () node.entity.addComponent (flash)
De InvulnerableState
klasse lijkt erg op de Normale staat
klasse. Het belangrijkste verschil is dat je bij het binnengaan van deze staat de knipperende component aan de entiteit van de speler toevoegt in plaats van deze te verwijderen.
Nu dat uw staatsklassen beide compleet en open zijn PlayerNode.swift nogmaals en voeg de volgende regels toe aan de PlayerNode
klasse:
var stateMachine: GKStateMachine! func enterNormalState () self.stateMachine.enterState (NormalState)
Dit codefragment voegt een nieuwe eigenschap toe aan de PlayerNode
klasse en implementeert een gemaksmethode om terug te gaan naar de normale status.
Nu open GameScene.swift en, aan het einde van de didMoveToView (_ :)
methode, voeg de volgende twee regels toe:
playerNode.stateMachine = GKStateMachine (toestanden: [NormalState (withNode: playerNode), InvulnerableState (withNode: playerNode)]) playerNode.stateMachine.enterState (NormalState)
In deze twee regels code maken we een nieuwe GKStateMachine
met de twee toestanden en vertel het om de Normale staat
.
Ten slotte, vervang de implementatie van de handleContactWithNode (_ :)
methode van de GameScene
klasse met de volgende implementatie:
func handleContactWithNode (contact: ContactNode) if contact is PointsNode NSNotificationCenter.defaultCenter (). postNotificationName ("updateScore", object: self, userInfo: ["score": 1]) else als contact RedEnemyNode && playerNode.stateMachine is. huidige toestand! is NormalState NSNotificationCenter.defaultCenter (). postNotificationName ("updateScore", object: self, userInfo: ["score": -2]) playerNode.stateMachine.enterState (InvulnerableState) playerNode.performSelector ("enterNormalState", metObject: nihil, afterDelay: 5.0) else if contact is YellowEnemyNode && playerNode.stateMachine.currentState! is NormalState self.playerNode.enabled = false contact.removeFromParent ()
Wanneer de blauwe stip van de speler in botsing komt met een rode vijandelijke stip, zal de speler de InvulnerableState
staat gedurende vijf seconden en keert dan terug naar de Normale staat
staat. We controleren ook wat de huidige status van de speler is en voeren alleen vijandige logica uit als dit het geval is Normale staat
staat.
Bouw en voer uw app nog een laatste keer uit en beweeg over de kaart totdat u een rode stip vindt. Wanneer je botst met de rode stip, zul je zien dat je blauwe stip zijn onkwetsbare toestand betreedt en gedurende vijf seconden knippert.
In deze tutorial heb ik je kennis laten maken met twee van de belangrijkste aspecten van het GameplayKit-framework, entiteiten en componenten, en state machines. Ik heb je laten zien hoe je entiteiten en componenten kunt gebruiken om je gamemodel te structureren en alles georganiseerd te houden. Het gebruik van componenten is een zeer eenvoudige manier om functionaliteit te delen tussen objecten in uw games.
Ik heb je ook de basis van staatsmachines laten zien, inclusief hoe je kunt specificeren in welke staten een bepaalde staat kan overstappen en hoe code kan worden uitgevoerd wanneer een bepaalde staat is ingevoerd.
Blijf op de hoogte voor het tweede deel van deze serie, waarin we dit spel naar een nieuw niveau zullen tillen door wat kunstmatige intelligentie toe te voegen, beter bekend als AI. De AI stelt vijandige stippen in staat om de speler te richten en het beste pad te vinden om de speler te bereiken.
Zoals altijd, als je opmerkingen of vragen hebt, laat ze dan achter in de reacties hieronder.