SpriteKit From Scratch fisica e collisioni

introduzione

In questa esercitazione, la terza parte della serie SpriteKit From Scratch, esaminiamo in dettaglio la funzionalità di simulazione fisica di SpriteKit e come questa può essere utilizzata nei tuoi giochi SpriteKit 2D.

Questo tutorial richiede che tu stia utilizzando Xcode 7.3 o versioni successive, che include Swift 2.2 e iOS 9.3, tvOS 9.2 e OS X 10.11.4 SDKs.

Per seguire, puoi utilizzare il progetto che hai creato nel precedente tutorial o scaricare una nuova copia da GitHub.

La grafica utilizzata per il gioco di questa serie può essere trovata su GraphicRiver. GraphicRiver è un'ottima fonte per trovare grafica e grafica per i tuoi giochi.

1. Mondi di fisica

La prima parte di qualsiasi simulazione fisica in SpriteKit è la physicsWorld proprietà della scena corrente. Questa proprietà è un SKPhysicsWorld oggetto che definisce proprietà come la gravità della scena, la velocità della simulazione fisica e il delegato di contatto. I mondi della fisica possono anche definire giunti tra oggetti per fissare efficacemente più nodi insieme in punti specifici.

Gravità

Per il vista dall'alto gioco di stile che stiamo creando in questa serie, vogliamo cambiare il valore di gravità predefinito fornito da SpriteKit. La gravità predefinita è intesa per a vista frontale gioco con un valore di (0, -9,8) che simula la gravità terrestre, cioè, 0 accelerazione orizzontale e accelerazione verso il basso di 9.8m / s². Per il nostro gioco, abbiamo bisogno 0 gravità verticale in modo che l'auto non inizi ad accelerare verso il basso una volta impostate le sue proprietà fisiche.

Aperto MainScene.sks e fare clic sullo sfondo grigio per selezionare la scena. Quindi, apri il Ispettore degli attributi e cambiare Gravità così che entrambi Xi componenti sono impostati su 0.

È importante notare che la gravità è l'unica proprietà del mondo della fisica che può essere modificata usando l'editor di scene di Xcode. Eventuali altre proprietà devono essere modificate a livello di codice.

Contatta Delegato

Affinché il gioco possa rilevare le collisioni tra gli oggetti, abbiamo bisogno di impostare il mondo della fisica della scena contactDelegate proprietà. Questo delegato può essere qualsiasi oggetto conforme al SKPhysicsContactDelegate protocollo. Questo protocollo definisce due metodi, didBeginContact (_ :) e didEndContact (_ :). È possibile utilizzare questi metodi per eseguire azioni basate sugli oggetti in collisione nella scena.

Per mantenere unito il nostro codice, faremo il MainScene esempio il suo delegato di contatto. Aperto MainScene.swift e modifica il MainScene definizione di classe conforme al SKPhysicsContactDelegate protocollo.

import UIKit import SpriteKit class MainScene: SKScene, SKPhysicsContactDelegate ...

Nel didMoveToView (_ :), abbiamo impostato il MainScene istanza come delegato di contatto del physicsWorld proprietà.

override func didMoveToView (visualizza: SKView) ... physicsWorld.contactDelegate = self

Implementeremo i metodi del SKPhysicsContactDelegate protocollo più tardi. Per prima cosa è necessario impostare le proprietà fisiche dei nodi nella scena.

2. Corpi Fisici

Qualsiasi nodo in SpriteKit a cui si desidera simulare la fisica in qualche modo deve essere assegnato a un unico SKPhysicsBody oggetto. I corpi fisici contengono diverse proprietà tra cui:

  • massa
  • densità
  • la zona
  • attrito
  • velocità

Nei tuoi giochi, puoi anche definire fino a 32 categorie uniche e un corpo fisico può essere assegnato a qualsiasi numero di queste categorie. Le categorie sono molto utili per determinare quali nodi della scena possono interagire tra loro in termini di collisioni.

Sui corpi fisici, queste categorie sono rappresentate dal categoryBitMaskcollisionBitMask proprietà, a cui sono entrambi dati 0xFFFFFFFF valore per impostazione predefinita. Ciò significa che tutti i nodi appartengono a tutte le categorie. È importante notare che in questo valore, ogni cifra esadecimale F è una forma abbreviata e rappresenta il numero 15 in cifre binarie (1111) ognuno dei quali corrisponde a una delle 32 categorie che è possibile utilizzare.

Quando due nodi si scontrano, un logico E operazione viene eseguita sul collisionBitMask e il categoryBitMask rispettivamente del primo e del secondo corpo. Se il risultato è un valore diverso da zero, SpriteKit esegue la simulazione sui due nodi a cui appartengono i corpi.

Si noti che questo E il calcolo viene eseguito due volte con i due corpi scambiati. Per esempio:

  • Calcolo 1: bodyA.collisionBitMask e bodyB.categoryBitMask
  • Calcolo 2: bodyB.collisionBitMask e bodyA.categoryBitMask

Se non sai come E l'operatore lavora, quindi ecco un esempio molto semplice scritto in Swift:

let mask1 = 0x000000FF let mask2 = 0x000000F0 let result = mask1 & mask2 // result = 0x000000F0

Il E l'operatore determina quali parti delle maschere di bit sono uguali e restituisce un nuovo valore di maschera di bit contenente le parti corrispondenti.

Una cosa importante da notare è che queste maschere bit hanno effetto solo sul lato SpriteKit della simulazione fisica e non vengono notificate le collisioni rilevate in questo modo. Ciò significa che i corpi possono interagire tra loro, ma nessuno dei metodi del delegato di contatto è chiamato.

Affinché questi metodi possano essere eseguiti, è necessario specificare a contactTestBitMask per ogni corpo, che produce un valore diverso da zero quando un E l'operatore agisce su di loro. Per tutti i corpi fisici, questa maschera bit ha un valore predefinito di 0x00000000 il che significa che non verrai avvisato di eventuali collisioni a cui il corpo fisico prende parte.

I corpi fisici, incluse le loro varie maschere di bit, possono essere impostati nell'editor di scene Xcode. Aperto MainScene.sks, seleziona l'auto e apri il Ispettore degli attributi sulla destra. Scorri verso il basso fino a Definizione della fisica sezione.

Perché Tipo di corpo è impostato per Nessuna, nessuna delle proprietà relative alla fisica è visibile. Per cambiare questo, abbiamo bisogno di impostare Tipo di corpo ad un valore diverso da Nessuna. Sono disponibili tre tipi di corpo:

  • rettangolo di delimitazione
  • cerchio di confine
  • maschera alfa
th

Questi tre tipi di corpo fisico sono i più comuni in SpriteKit. Rettangolo delimitato e Circonferenza lavorare creando una barriera attorno allo sprite da utilizzare nelle simulazioni di fisica. Ciò significa che lo sprite collide con un altro nodo ogni volta che la sua forma di delimitazione colpisce il corpo fisico di un altro nodo.

Il bordo di un rettangolo di delimitazione è esattamente uguale alla dimensione del nodo mostrato nell'editor di scena. Se si seleziona Circonferenza, tuttavia, si vede un sottile cerchio azzurro che rappresenta la forma del cerchio di delimitazione.

Maschera alfa funziona un po 'diversamente e guarda la reale consistenza dell'immagine dello sprite per determinare i bordi del corpo fisico. Questo tipo di corpo è di gran lunga il più preciso in SpriteKit, ma può avere un grande impatto sulle prestazioni del tuo gioco, in particolare quando si utilizzano sprite con forme complesse.

Per il nostro gioco, dal momento che stiamo usando solo una macchina sprite e la nostra scena non è particolarmente complessa, useremo il Maschera alfa tipo di corpo. È non consigliato di usare questo tipo di corpo per tutti gli sprite nella tua scena anche se è il più preciso. Quando selezioni questa opzione dal menu a discesa, dovresti vedere una linea blu chiara che appare attorno al bordo dell'auto.

È importante notare che altri tipi di corpi fisici possono essere creati a livello di programmazione, come i corpi da CGPath oggetti così come cerchi e rettangoli di dimensioni personalizzate.

Ancora nel Ispettore degli attributi, ora dovresti vedere più opzioni disponibili nel Definizione della fisica sezione. L'unica proprietà che dobbiamo cambiare è la Maschera di contatto. Cambia questo a un valore di 1.

Con il corpo della fisica della macchina impostato, possiamo iniziare a mettere alcuni ostacoli nel gioco per entrare in collisione con la macchina.

3. Rilevare le collisioni

Prima di implementare i metodi di SKPhysicsContactDelegate protocollo, abbiamo bisogno di aggiungere alcuni ostacoli per l'auto da evitare. Per fare questo, genereremo un nuovo ostacolo ogni tre secondi davanti alla macchina e posizioneremo l'ostacolo in una corsia casuale.

Aperto MainScene.swift e aggiungere una dichiarazione di importazione per GameplayKit quadro in modo da poter utilizzare i generatori di numeri casuali forniti da GameplayKit.

Importa GameplayKit

Quindi, aggiungere il seguente metodo al MainScene classe:

func spawnObstacle (timer: NSTimer) if player.hidden timer.invalidate () return let spriteGenerator = GKShuffledDistribution (lowestValue: 1, highestValue: 2) let obstacle = SKSpriteNode (imageNamed: "Obstacle \ (spriteGenerator.nextInt ()) ") obstacle.xScale = 0.3 obstacle.yScale = 0.3 let physicsBody = SKPhysicsBody (circleOfRadius: 15) physicsBody.contactTestBitMask = 0x00000001 physicsBody.pinned = true physicsBody.allowsRotation = false obstacle.physicsBody = physicsBody let center = size.width / 2.0, difference = CGFloat (85.0) var x: CGFloat = 0 let laneGenerator = GKShuffledDistribution (lowestValue: 1, highestValue: 3) switch laneGenerator.nextInt () caso 1: x = centro - differenza caso 2: x = centro caso 3: x = centro + differenza predefinita: fatalError ("Numero al di fuori di [1, 3] generato") obstacle.position = CGPoint (x: x, y: (player.position.y + 800)) addChild (ostacolo)

Questo metodo è invocato ogni tre secondi. Scopriamolo per vedere cosa sta succedendo. Se il nodo del giocatore corrente è nascosto, il che è vero quando l'auto colpisce un ostacolo, allora invalideremo il timer e impediremo di generare ostacoli.

Otteniamo un numero casuale tra 1 e 2, e usarlo per creare un nodo sprite con uno dei due sprite di ostacolo disponibili nel progetto. Quindi modifichiamo la scala dell'ostacolo in modo che siano abbastanza piccoli da consentire all'auto di manovrare.

Successivamente, creiamo un corpo fisico per questo nuovo ostacolo con un cerchio con un raggio di 15 e una maschera di contatto di 0x00000001. Prepariamo appuntato a vero e allowsRotation a falso in modo che l'ostacolo rimanga sul posto e non si muova. Il corpo fisico viene quindi assegnato all'ostacolo.

Generiamo un altro numero casuale tra 1 e 3 per determinare su quale corsia deve essere posizionato l'ostacolo e dare all'ostacolo la sua posizione calcolata, aggiungendolo alla scena.

Si noti che la differenza orizzontale, 85, usato in spawnObstacle (_ :) è diverso da quello usato quando si sposta l'auto, 70. Lo facciamo per consentire un po 'più di spazio per la macchina per spostarsi tra ostacoli.

Con la logica di spawning degli ostacoli in atto, possiamo aggiungere il seguente codice alla fine del didMoveToView (_ :) metodo.

override func didMoveToView (visualizza: SKView) ... let timer = NSTimer (timeInterval: 3.0, target: self, selector: #selector (spawnInObstacle (_ :)), userInfo: nil, ripete: true) NSRunLoop.mainRunLoop (). addTimer (timer, forMode: NSRunLoopCommonModes) lascia camera = SKCameraNode () self.camera = camera camera.position = CGPoint (x: centro, y: player.position.y + 200) lascia moveForward = SKAction.moveBy (CGVectorMake (0, 100 ), durata: 1.0) camera.runAction (SKAction.repeatActionForever (moveForward)) addChild (camera) player.xScale = 0.4; player.yScale = 0.4 // Rende l'auto più piccola per adattarsi meglio tra gli ostacoli

Creiamo un timer da eseguire spawnObstacle (_ :) ogni tre secondi e aggiungilo al ciclo di esecuzione principale. Creiamo anche un SKCameraNode agire come la macchina da presa per la scena e assegnarla al telecamera proprietà della scena. Ciò fa sì che la scena sia resa dal punto di vista del nodo di questa telecamera. Si noti che la fotocamera è centrata orizzontalmente e leggermente sopra l'auto.

Aggiungiamo anche un'azione identica alla fotocamera in modo che rimanga allineata con la macchina. Aggiungiamo la fotocamera come un nodo figlio della scena come qualsiasi altro nodo normale. Ultimo ma non meno importante, ridimensioniamo la macchina in modo che possa adattarsi un po 'meglio agli ostacoli.

L'ultima parte per il rilevamento delle collisioni sta implementando uno dei due SKPhysicsContactDelegate metodi di protocollo. Nel nostro caso, implementeremo il didBeginContact (_ :) metodo come vogliamo essere notificati non appena l'auto colpisce un ostacolo. Aggiungi il seguente metodo al MainScene classe.

func didBeginContact (contatto: SKPhysicsContact) if contact.bodyA.node == player || contact.bodyB.node == player player.hidden = true player.removeAllActions () camera? .removeAllActions ()

Puoi vedere che questo metodo ha uno SKPhysicsContact parametro passato ad esso. Questo oggetto contiene informazioni chiave sulla collisione, inclusa la direzione, l'impulso e gli oggetti coinvolti.

In questo codice, ci interessa solo quali nodi sono coinvolti nella collisione. Controlliamo se qualcuno di loro è l'auto e, se è vero, nasconde l'auto nella scena e termina il movimento dell'auto e della telecamera.

Crea ed esegui la tua app e gioca al tuo gioco. Vedrai che, quando incontri un ostacolo, l'auto scompare e la scena si ferma.

Conclusione

Ora dovresti sapere come configurare i corpi fisici per i nodi nella scena e usarli per simulare la fisica realistica. Dovresti anche essere a tuo agio nell'impostare il rilevamento delle collisioni nella tua scena definendo un delegato di contatto e assegnare maschere di bit di test di contatto per i nodi che ti aspetti di scontrare tra loro.

Nel prossimo tutorial di SpriteKit From Scratch, esamineremo le funzionalità visive più avanzate di SpriteKit, inclusi sistemi di particelle, luci e filtri.

Come sempre, assicurati di lasciare i tuoi commenti e feedback nei commenti qui sotto.