Combinando la potenza di SpriteKit e SceneKit

Cosa starai creando

introduzione

SpriteKit e SceneKit sono framework iOS progettati per facilitare agli sviluppatori la creazione di risorse 2D e 3D nei giochi casuali. In questo tutorial, ti mostrerò come combinare il contenuto creato in entrambi i framework in un'unica vista per utilizzare le API che Apple ha reso disponibile.

Otterrai questo risultato aggiungendo un semplice pulsante di riproduzione / pausa e funzionalità di mantenimento del punteggio in una scena 3D SceneKit attraverso l'uso di un'interfaccia 2D basata su SpriteKit.

Questo tutorial richiede che tu stia utilizzando almeno Xcode 6+ e che abbia esperienza precedente con i contenuti SpriteKit e SceneKit di base. Altrimenti, ti consiglio di leggere prima alcuni dei nostri Tuts + tutorial su SpriteKit e SceneKit.

Si consiglia inoltre di utilizzare un dispositivo fisico iOS per il test, che richiede un account sviluppatore iOS attivo e a pagamento. Dovrai anche scaricare il progetto iniziale da GitHub.

1. Impostazione del progetto

Quando apri il progetto iniziale, vedrai che, oltre al valore predefinito AppDelegateViewController classi, hai anche altre due classi, MainSceneOverlayScene.

Il MainScene la classe è una sottoclasse di SCNScene e fornisce il contenuto 3D della tua app. Allo stesso modo, il OverlayScene la classe è una sottoclasse di SKScene e contiene i contenuti SpriteKit 2D nella tua app.

Sentiti libero di guardare alle implementazioni di queste classi. Dovrebbe sembrare familiare se hai qualche esperienza con SpriteKit e SceneKit. Nel tuo ViewController La classe di viewDidLoad metodo, troverai anche il codice per l'impostazione di base SCNView esempio.

Crea ed esegui la tua app su iOS Simulator o sul tuo dispositivo fisico. In questa fase, la tua app contiene un cubo rotante blu.

È ora di aggiungere una scena SpriteKit in cima al contenuto 3D. Questo viene fatto impostando il overlaySKScene proprietà su qualsiasi oggetto conforme al SCNSceneRenderer protocollo, nel nostro esempio è il SCNView esempio. Nel ViewController.swift aggiungi le seguenti linee al viewDidLoad metodo:

override func viewDidLoad () ... self.spriteScene = OverlayScene (dimensione: self.view.bounds.size) self.sceneView.overlaySKScene = self.spriteScene

Quando crei ed esegui di nuovo la tua app, vedrai che ora hai un pulsante di pausa posizionato in basso a sinistra e un'etichetta di punteggio nella parte inferiore centrale della visualizzazione della tua app.

Forse ti starai chiedendo perché è meglio usare questa proprietà piuttosto che semplicemente aggiungere un SKView come una sottoview del SCNView oggetto. Quando il overlaySKScene viene utilizzata la proprietà, sia i componenti 2D che 3D della tua app utilizzano lo stesso contesto OpenGL per il rendering dei contenuti sullo schermo. Ciò si comporta in modo significativamente migliore rispetto alla creazione di due viste separate, ognuna con un proprio contesto OpenGL e una pipeline di rendering. Anche se la differenza è trascurabile per questa semplice configurazione, le prestazioni ottenute in lager, scene più complesse possono essere inestimabili.

2. Interazione con SceneKit e SpriteKit

Ci sono molti modi diversi in cui puoi trasferire le informazioni tra i tuoi MainSceneOverlayScene le istanze. In questo tutorial, utilizzerai l'osservazione dei valori-chiave, in breve KVO.

Prima di implementare la possibilità di sospendere l'animazione del cubo, è necessario prima aggiungere questa funzionalità alla scena SpriteKit. Nel OverlayScene.swift, aggiungi il seguente metodo al OverlayScene classe:

override func touchesEnded (tocca: imposta, Evento withEvent: UIEvent) let touch = touches.first as? UITouch lascia location = touch? .LocationInNode (self) se self.pauseNode.containsPoint (posizione!) If! Self.paused self.pauseNode.texture = SKTexture (imageNamed: "Play Button") else self.pauseNode. texture = SKTexture (imageNamed: "Pulsante di pausa") self.paused =! self.paused

Il touchesEnded (_: withEvent :) il metodo viene richiamato quando l'utente solleva il dito (s) dallo schermo del dispositivo. In questo metodo, si controlla se l'utente ha toccato il pulsante di pausa e aggiornare la scena di conseguenza. Crea ed esegui nuovamente la tua app per verificare che questo pezzo del puzzle funzioni correttamente.

Ora abbiamo bisogno di interrompere il cubo 3D dalla rotazione quando il pulsante di pausa è stato toccato dall'utente. Ritornare a ViewController.swift e aggiungi la seguente riga al viewDidLoad metodo:

override func viewDidLoad () ... self.spriteScene.addObserver (self.sceneView.scene !, forKeyPath: "paused", opzioni: .New, context: nil)

Infine, aggiungi il seguente metodo a MainScene.swift per abilitare l'osservazione dei valori-chiave:

override func observValueForKeyPath (keyPath: String, ofObject object: AnyObject, modifica: [NSObject: AnyObject], contesto: UnsafeMutablePointer) if keyPath == "paused" self.paused = change [NSKeyValueChangeNewKey] as! Bool

Se si crea e si esegue nuovamente l'app, il cubo dovrebbe smettere di ruotare quando si tocca il pulsante di pausa.

Proprio come le informazioni possono essere trasferite da una scena SpriteKit a una scena di SceneKit, è anche possibile inviare dati dalle istanze di SceneKit alle istanze di SpriteKit. In questa semplice app, stai per aggiungere un punto al punteggio ogni volta che il cubo viene toccato dall'utente. Nel ViewController.swift, aggiungi il seguente metodo:

override func touchesEnded (tocca: imposta, Evento withEvent: UIEvent) let touch = touches.first as? UITouch lascia location = touch? .LocationInView (self.sceneView) lascia hitResults = self.sceneView.hitTest (posizione !, opzioni: nil) per il risultato in (hitResults as! [SCNHitTestResult]) if result.node == (self. sceneView.scene as! MainScene) .cubeNode self.spriteScene.score + = 1

Si noti che abbiamo usato il touchesEnded (_: withEvent :) metodo piuttosto che a UITapGestureRecognizer, perché UIGestureRecognizer gli oggetti causano il touchesEnded (_: withEvent :) metodo da eseguire in modo incoerente. Poiché si utilizza questo metodo per il pulsante di pausa, è necessario essere sicuri che verrà chiamato ogni volta che l'utente tocca lo schermo.

Nel touchesEnded (_: withEvent :) metodo, eseguiamo un hit test per la posizione finale di un tocco nel tuo sceneView. Se la posizione del tocco corrisponde alla posizione del cubo, un punto viene aggiunto al punteggio del tuo spriteScene. Il testo nella scena si aggiornerà automaticamente grazie all'osservatore delle proprietà del Punto proprietà nel OverlayScene classe.

Esegui nuovamente l'app e tocca il cubo per verificare che l'etichetta del punteggio sia aggiornata ogni volta che il cubo viene toccato.

Sia il pulsante di pausa che l'etichetta del punteggio esemplificano il modo in cui è possibile trasferire le informazioni tra le scene di SpriteKit e SceneKit. Questa informazione, tuttavia, non è limitata ai valori numerici e booleani, può essere qualunque sia il tipo di dati richiesto dal progetto.

3. Utilizzo di scene SpriteKit come materiali SceneKit

Oltre ad essere in grado di sovrapporre una scena SpriteKit su una scena di SceneKit, puoi anche usare un SKScene istanza come materiale per la geometria di SceneKit. Questo viene fatto assegnando un SKScene oggetto al contenuto proprietà di un SCNMaterialProperty oggetto. Questo ti consente di aggiungere facilmente un materiale animato su qualsiasi oggetto 3D.

Diamo un'occhiata a un esempio. Nella tua app, stai per aggiungere una semplice animazione di transizione del colore al cubo invece del blu statico che ha attualmente. Nel dentro metodo del MainScene classe, sostituire il seguente blocco di codice:

override init () super.init () lascia cube = SCNBox (larghezza: 3, altezza: 3, lunghezza: 3, chamferRadius: 0) lascia cubeMaterial = SCNMaterial () cubeMaterial.diffuse.contents = UIColor.blueColor () cube. materials = [cubeMaterial] self.cubeNode = SCNNode (geometry: cube) self.cubeNode.runAction (SCNAction.repeatActionForever (SCNAction.rotateByX (0, y: 0.01, z: 0, duration: 1.0 / 60.0))) ...

con questo blocco di codice:

override init () super.init () lascia cube = SCNBox (larghezza: 3, altezza: 3, lunghezza: 3, chamferRadius: 0) lascia materialScene = SKScene (dimensione: CGSize (larghezza: 100, altezza: 100)) lascia backgroundNode = SKSpriteNode (color: UIColor.blueColor (), dimensione: materialScene.size) backgroundNode.position = CGPoint (x: materialScene.size.width / 2.0, y: materialScene.size.height / 2.0) materialScene.addChild (backgroundNode) let blueAction = SKAction.colorizeWithColor (UIColor.blueColor (), colorBlendFactor: 1, duration: 1) let redAction = SKAction.colorizeWithColor (UIColor.redColor (), colorBlendFactor: 1, duration: 1) let greenAction = SKAction.colorizeWithColor (UIColor .greenColor (), colorBlendFactor: 1, duration: 1) backgroundNode.runAction (SKAction.repeatActionForever (SKAction.sequence ([blueAction, redAction, greenAction]))) lascia cubeMaterial = SCNMaterial () cubeMaterial.diffuse.contents = materialScene cube. materials = [cubeMaterial] self.cubeNode = SCNNode (geometry: cube) self.cubeNode.runAction (SCNAction.repeatAction Forever (SCNAction.rotateByX (0, y: 0.01, z: 0, duration: 1.0 / 60.0))) ...

Questo snippet di codice crea un semplice SKScene istanza con un nodo di sfondo, che inizialmente è blu. Creiamo quindi tre azioni per la transizione dal blu al rosso e al verde. Eseguiamo le azioni in una sequenza ripetuta sul nodo di sfondo.

Mentre abbiamo usato i colori base nel nostro esempio, tutto ciò che può essere fatto in una scena SpriteKit può essere trasformato in un materiale di SceneKit.

Crea ed esegui l'app un'ultima volta. Vedrai che il colore del cubo passa dal blu al rosso e al verde.

Nota che mettere in pausa la scena di SceneKit non mette in pausa la scena SpriteKit che abbiamo usato come materiale. Per mettere in pausa l'animazione del materiale, è necessario mantenere un riferimento alla scena SpriteKit e metterla in pausa esplicitamente.

Conclusione

La combinazione dei contenuti di SpriteKit e SceneKit può essere molto utile in vari modi. La sovrapposizione di una scena 2D sulla parte superiore di una scena 3D consente di creare un'interfaccia dinamica e determinerà un miglioramento delle prestazioni poiché entrambe le scene utilizzano lo stesso contesto OpenGL e la stessa pipeline di rendering. Hai anche imparato a sfruttare SpriteKit per creare materiali animati per oggetti 3D. Se avete commenti o domande, lasciateli nei commenti qui sotto.