Un'introduzione a GameplayKit Parte 1

introduzione

Accanto a tutte le nuove funzionalità e framework in iOS 9 e OS X El Capitan, con le versioni di quest'anno Apple ha anche creato un framework completamente nuovo per gli sviluppatori di giochi, GameplayKit. Con le API grafiche esistenti (SpriteKit, SceneKit e Metal) che semplificano la creazione di giochi di grande impatto su iOS e OS X, Apple ha ora rilasciato GameplayKit per semplificare la creazione di giochi che giocare bene. Questo nuovo framework contiene molte classi e funzionalità che possono essere utilizzate per aggiungere facilmente logica complessa ai tuoi giochi.

In questo primo tutorial, ti spiegherò due aspetti principali del framework GameplayKt:

  • entità e componenti
  • macchine di stato

Prerequisiti

Questo tutorial richiede che tu stia correndo Xcode 7 sopra OS X Yosemite o più tardi. Sebbene non sia richiesto, si consiglia di far funzionare un dispositivo fisico iOS 9 poiché otterrai prestazioni molto migliori durante il test del gioco basato su SpriteKit utilizzato in questo tutorial.

1. Per iniziare

Per prima cosa devi scaricare il progetto di avvio per questa serie di tutorial da GitHub. Dopo averlo fatto, apri il progetto in Xcode ed eseguilo su iOS Simulator o sul tuo dispositivo.

Vedrai che è un gioco molto semplice in cui controlli un punto blu e navighi sulla mappa. Quando ti scontri con un punto rosso, perdi due punti. Quando ti scontri con un punto verde, guadagni un punto. Quando entri in collisione con un punto giallo, il tuo punto viene congelato per un paio di secondi.

In questa fase, è un gioco molto semplice, ma nel corso di questa serie di tutorial, e con l'aiuto di GameplayKit, aggiungeremo molte più funzionalità ed elementi di gameplay.

2. Entità e componenti

Il primo aspetto importante del nuovo framework GameplayKit è basato su un concetto di strutturazione del codice entitàcomponenti. Funziona consentendo a te, lo sviluppatore, di scrivere codice comune utilizzato da molti tipi di oggetti diversi nel tuo gioco mantenendolo ben organizzato e gestibile. Il concetto di entità e componenti ha lo scopo di eliminare l'approccio basato sull'eredità comune per condividere funzionalità comuni tra i tipi di oggetto. Il modo più semplice per comprendere questo concetto è con alcuni esempi, quindi immagina il seguente scenario:

Stai costruendo un gioco di difesa della torre con tre tipi principali di torri, Fuoco, Ghiaccio, e Guarire. I tre tipi di torri condividono alcuni dati comuni, come la salute, le dimensioni e la forza. Le tue torri Fuoco e Ghiaccio devono essere in grado di colpire i nemici in arrivo per sparare mentre la tua torre Heal no. Tutto ciò che la tua torre Heal deve fare è riparare le tue altre torri entro un certo raggio quando subiscono danni.

Con questo modello di gioco di base in mente, vediamo come il tuo codice potrebbe essere organizzato usando un eredità struttura:

  • Un genitore Torre classe contenente dati comuni quali salute, dimensioni e forza.
  • FiretowerIceTower, e HealTower classi che erediterebbero dal Torre classe.
  • Nel HealTower classe, hai la logica responsabile per la cura delle tue altre torri entro un certo raggio.

Finora, questa struttura è a posto, ma ora sorge un problema quando è necessario implementare l'abilità di targeting delle torri di fuoco e ghiaccio. Ti basta copiare e incollare lo stesso codice in entrambi i tuoi FiretowerIceTower classi? Se è necessario apportare modifiche, sarà necessario modificare il codice in più di un posto, che è noioso e soggetto a errori. Oltre a ciò, cosa succede se vuoi aggiungere un nuovo tipo di torre che ha anche bisogno di questa funzionalità di targeting. Lo copia e incolla una terza volta?

Il modo migliore sembra essere mettere questa logica di targeting nel genitore Torre classe. Ciò consentirebbe di avere solo una copia del codice che deve essere modificata in un unico posto. Aggiungere questo codice qui, tuttavia, renderebbe il Torre classe molto più grande e più complicata di quanto deve essere quando non tutte le sue sottoclassi hanno bisogno di quella funzionalità. Se vuoi anche aggiungere più funzionalità condivise tra i tuoi tipi di torre, il tuo Torre la classe diventerebbe gradualmente sempre più grande, il che renderebbe difficile la sua collaborazione.

Come puoi vedere, mentre è possibile creare un modello di gioco basato su eredità, può diventare molto rapidamente e facilmente disorganizzato e difficile da gestire.

Ora, vediamo come questo stesso modello di gioco può essere strutturato usando entità componenti:

  • Creeremmo FiretowerIceTower, e HealTower entità. Altre entità potrebbero essere create per altri tipi di torri che si desidera aggiungere in seguito.
  • Creeremmo anche a BasicTower componente che dovrebbe contenere salute, dimensioni, forza, ecc.
  • Per gestire la guarigione delle tue torri entro un certo raggio, aggiungeremmo un Guarigione componente.
  • UN Targeting componente dovrebbe contenere il codice necessario per colpire i nemici in arrivo.

Usando GameplayKit e questa struttura avresti quindi un tipo di entità unico per ogni tipo di torre nel tuo gioco. Per ogni singola entità, è possibile aggiungere i componenti desiderati che si desidera. Per esempio:

  • Il tuo Firetower e IceTower le entità avrebbero ciascuno un BasicTower e Targeting componente ad esso collegato.
  • Il tuo HealTower entità avrebbe entrambi a BasicTower e a Guarigione componente.

Come puoi vedere, usando una struttura basata su entità e componenti, il tuo modello di gioco è ora molto più semplice e versatile. La tua logica di targeting deve essere scritta una volta sola e si collega solo alle entità di cui ha bisogno. Allo stesso modo, i tuoi dati di base della torre possono ancora essere facilmente condivisi tra tutte le tue torri senza mettere insieme tutte le altre funzionalità comuni.

Un'altra cosa fantastica di questa struttura basata su entità e componenti è che i componenti possono essere aggiunti e rimossi dalle entità ogni volta che lo si desidera. Ad esempio, se si desidera disabilitare le torri Heal in determinate condizioni, è sufficiente rimuovere il Guarigione componente dalla tua entità fino a quando non vengono soddisfatte le condizioni giuste. Allo stesso modo, se volessi una delle tue torri di fuoco per ottenere un'abilità di guarigione temporanea, potresti semplicemente aggiungere un Guarigione componente al tuo Firetower entità per un determinato periodo di tempo.

Ora che sei a tuo agio con il concetto di una struttura del modello di gioco basata su entità e componenti, crearne uno nel nostro stesso gioco. In Xcode File Inspector, trovare la Entità cartella all'interno del tuo progetto. Per comodità, ci sono già tre classi di entità per te, ma ora creerai una nuova entità da zero.

Scegliere File> Nuovo> File ... o premere Comando-N per creare una nuova classe. Assicurati di selezionare il Cocoa Touch Class modello dal iOS> Origine sezione. Dai un nome alla classe Giocatore e renderlo una sottoclasse di GKEntity.

Vedrai che immediatamente all'apertura del tuo nuovo file Xcode verrà visualizzato un errore. Per risolvere questo problema, aggiungi la seguente dichiarazione di importazione sotto quella esistente importa UIKit dichiarazione:

Importa GameplayKit

Ritornare a PlayerNode.swift e aggiungere la seguente proprietà al PlayerNode classe:

var entity = Player ()

Quindi, vai a componenti cartella nel tuo progetto Xcode e crea una nuova classe come hai fatto prima. Questa volta, dai il nome alla classe FlashingComponent e renderlo una sottoclasse di GKComponent come mostrato di seguito.

Il componente che hai appena creato gestirà il lampeggio visivo del nostro punto blu quando viene colpito da un punto rosso e si trova nel suo stato invulnerabile. Sostituisci il contenuto di FlashingComponent.swift con il seguente:

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")

L'implementazione mantiene semplicemente un riferimento a un SKNode oggetto e ripete dissolvenza in entrata e dissolvenza in sequenza finché il componente è attivo.

Ritornare a GameScene.swift e aggiungi il seguente codice da qualche parte all'interno del didMoveToView (_ :) metodo:

// Aggiunta del componente let flash = FlashingComponent () flash.nodeToFlash = playerNode flash.startFlashing () playerNode.entity.addComponent (flash)

Creiamo a FlashingComponent oggetto e impostarlo per eseguire il suo lampeggio sul punto del giocatore. L'ultima riga quindi aggiunge il componente all'entità per mantenerlo attivo ed in esecuzione.

Crea ed esegui la tua app. Ora vedrai che il tuo punto blu si dissolve lentamente verso l'interno e l'esterno ripetutamente.

Prima di proseguire, elimina il codice che hai appena aggiunto didMoveToView (_ :) metodo. Successivamente, si aggiungerà questo codice ma solo quando il punto blu entrerà nel suo stato invulnerabile.

3. Macchine di stato

In GameplayKit, le macchine a stati forniscono un modo per identificare ed eseguire facilmente attività basate sulla corrente stato di un oggetto particolare. Prendendo spunto dal precedente esempio di difesa della torre, potrebbero includere alcuni stati possibili per ogni torre Attivo, Disabilitato, e Distrutto. Uno dei principali vantaggi delle macchine a stati è che è possibile specificare in quali stati può spostarsi un altro stato. Con i tre stati di esempio menzionati sopra, utilizzando una macchina a stati, è possibile impostare la macchina a stati in modo che:

  • una torre può diventare Disabilitato quando Attivo e viceversa
  • una torre può diventare Distrutto quando entrambi Attivo o Disabilitato
  • una torre può non diventare AttivoDisabilitato una volta è stato Distrutto

Nel gioco per questo tutorial, lo terremo molto semplice e avremo solo un normale invulnerabile stato.

Nel tuo progetto State Machine cartella, creare due nuove classi. Nominali NormalStateInvulnerableState rispettivamente, essendo entrambi una sottoclasse di GKState classe.

Sostituisci il contenuto di NormalState.swift con il seguente:

import UIKit import SpriteKit import GameplayKit class NormalState: GKState var node: PlayerNode init (withNode: PlayerNode) node = withNode override func isValidNextState (stateClass: AnyClass) -> Bool switch stateClass case è InvulnerableState.Type: return true default : return false override func didEnterWithPreviousState (previousState: GKState?) if let _ = previousState as? InvulnerableState node.entity.removeComponentForClass (FlashingComponent) node.runAction (SKAction.fadeInWithDuration (0.5))

Il NormalState la classe contiene quanto segue:

  • Implementa un semplice inizializzatore per mantenere un riferimento al nodo del giocatore corrente.
  • Ha un'implementazione per il isValidNextState (_ :) metodo. L'implementazione di questo metodo restituisce un valore booleano, che indica se la classe di stato corrente può spostarsi o meno nella classe di stato fornita dal parametro method.
  • La classe include anche un'implementazione per il didEnterWithPreviousState (_ :) metodo di callback. Nell'implementazione del metodo, controlliamo se lo stato precedente era il InvulnerableState stato e, se è vero, rimuove il componente lampeggiante dall'entità del giocatore.

Ora aperto InvulnerableState.swift e sostituire il suo contenuto con il seguente:

import UIKit import GameplayKit class InvulnerableState: GKState var node: PlayerNode init (withNode: PlayerNode) node = withNode function override isValidNextState (stateClass: AnyClass) -> Bool switch stateClass case is NormalState.Type: return true default: return false override func didEnterWithPreviousState (previousState: GKState?) if let _ = previousState as? NormalState // Aggiunta del componente let flash = FlashingComponent () flash.nodeToFlash = node flash.startFlashing () node.entity.addComponent (flash)

Il InvulnerableState la classe è molto simile al NormalState classe. La differenza principale è che entrando in questo stato si aggiunge il componente lampeggiante all'entità del giocatore piuttosto che rimuoverlo.

Ora che le tue lezioni di stato sono entrambe complete, aperte PlayerNode.swift di nuovo e aggiungere le seguenti righe al PlayerNode classe:

var stateMachine: GKStateMachine! func enterNormalState () self.stateMachine.enterState (NormalState)

Questo snippet di codice aggiunge una nuova proprietà a PlayerNode classe e implementa un metodo di convenienza per tornare allo stato normale.

Ora aperto GameScene.swift e, alla fine del didMoveToView (_ :) metodo, aggiungere le seguenti due righe:

playerNode.stateMachine = GKStateMachine (stati: [NormalState (withNode: playerNode), InvulnerableState (withNode: playerNode)]) playerNode.stateMachine.enterState (NormalState)

In queste due righe di codice, ne creiamo una nuova GKStateMachine con i due stati e digli di entrare nel NormalState.

Infine, sostituire l'implementazione del handleContactWithNode (_ :) metodo del GameScene classe con la seguente implementazione:

func handleContactWithNode (contact: ContactNode) se contact è PointsNode NSNotificationCenter.defaultCenter (). postNotificationName ("updateScore", object: self, userInfo: ["score": 1]) else if contact è RedEnemyNode && playerNode.stateMachine. stato attuale! è NormalState NSNotificationCenter.defaultCenter (). postNotificationName ("updateScore", oggetto: self, userInfo: ["score": -2]) playerNode.stateMachine.enterState (InvulnerableState) playerNode.performSelector ("enterNormalState", withObject: nil, afterDelay: 5.0) else if contact is YellowEnemyNode && playerNode.stateMachine.currentState! è NormalState self.playerNode.enabled = false contact.removeFromParent ()

Quando il punto blu del giocatore si scontra con un punto nemico rosso, il giocatore entra nel InvulnerableState stato per cinque secondi e poi tornare al NormalState stato. Controlliamo anche qual è lo stato attuale del giocatore e eseguiamo solo la logica relativa al nemico se è il NormalState stato.

Crea ed esegui l'app un'ultima volta e spostati sulla mappa finché non trovi un punto rosso. Quando ti scontri con il punto rosso, vedrai che il tuo punto blu entra nel suo stato invulnerabile e lampeggia per cinque secondi.

Conclusione

In questo tutorial, ti ho presentato due degli aspetti principali del framework GameplayKit, entità e componenti, e macchine di stato. Ti ho mostrato come puoi utilizzare entità e componenti per strutturare il tuo modello di gioco e mantenere tutto organizzato. L'utilizzo di componenti è un modo molto semplice per condividere la funzionalità tra gli oggetti nei tuoi giochi.

Vi ho anche mostrato le basi delle macchine di stato, compreso il modo in cui è possibile specificare quali stati possono essere sottoposti a un particolare stato e come eseguire codice quando viene inserito un particolare stato.

Rimanete sintonizzati per la seconda parte di questa serie in cui porteremo questo gioco ad un altro livello aggiungendo un po 'di intelligenza artificiale, meglio conosciuta come AI. L'IA consentirà ai punti nemici di colpire il giocatore e trovare il percorso migliore per raggiungere il giocatore.

Come sempre, se avete commenti o domande, lasciateli nei commenti qui sotto.