SpriteKit från scratch fysik och kollisioner

Introduktion

I den här handledningen, den tredje delen av SpriteKit From Scratch-serien, tar vi en detaljerad inblick i fysikens simuleringsfunktionalitet i SpriteKit och hur det kan användas i dina 2D SpriteKit-spel.

Denna handledning kräver att du kör Xcode 7.3 eller senare, som inkluderar Swift 2.2 och iOS 9.3, tvOS 9.2 och OS X 10.11.4 SDK.

För att följa med kan du antingen använda det projekt du skapade i föregående handledning eller ladda ner en ny kopia från GitHub.

Grafiken som används för spelet i denna serie finns på GraphicRiver. GraphicRiver är en bra källa för att hitta konstverk och grafik för dina spel.

1. Fysikvärldar

Den första delen av någon fysik simulering i SpriteKit är physicsWorld egenskapen för den aktuella scenen. Denna fastighet är en SKPhysicsWorld objekt som definierar egenskaper som din scens gravitation, simuleringshastighet och kontaktperson. Fysikvärldar kan också definiera leder mellan objekt för att effektivt knyta flera noder tillsammans vid specifika punkter.

Allvar

För toppvy stilspel vi skapar i denna serie vill vi ändra standardgravititetsvärdet från SpriteKit. Standard gravitationen är avsedd för a frontvy spel med ett värde av (0, -9,8) som simulerar jordens gravitation, det vill säga, 0 horisontell acceleration och en nedåtgående acceleration av 9,8 m / s ^. För vårt spel behöver vi 0 vertikal gravitation så att bilen inte börjar accelerera nedåt när vi ställer in sina fysikaliska egenskaper.

Öppna MainScene.sks och klicka på den grå bakgrunden för att välja scenen. Öppna sedan Attribut Inspector och ändra Allvar så att både X och komponenterna är inställda på 0.

Det är viktigt att notera att gravitationen är den enda fysikvärlden som kan ändras med hjälp av Xcodes scenredigerare. Alla andra egenskaper måste ändras programmatiskt.

Kontakta delegat

För att spelet ska kunna upptäcka kollisioner mellan objekt måste vi ställa in scenens fysikvärld contactDelegate fast egendom. Denna delegat kan vara något objekt som överensstämmer med SKPhysicsContactDelegate protokoll. Detta protokoll definierar två metoder, didBeginContact (_ :) och didEndContact (_ :). Du kan använda dessa metoder för att utföra åtgärder baserat på de objekt som kolliderar i scenen.

För att hålla vår kod tillsammans, ska vi göra MainScene Exempel på egen kontaktperson. Öppna MainScene.swift och redigera MainScene klassdefinition för att överensstämma med SKPhysicsContactDelegate protokoll.

importera UIKit import SpriteKit klass MainScene: SKScene, SKPhysicsContactDelegate ...

didMoveToView (_ :), vi satte MainScene Exempel som kontaktperson för physicsWorld fast egendom.

åsidosätta func didMoveToView (visa: SKView) ... physicsWorld.contactDelegate = self

Vi kommer att genomföra metoderna för SKPhysicsContactDelegate protokoll senare. Vi behöver först konfigurera fysikegenskaperna hos noderna i scenen.

2. Fysikorgan

Varje nod i SpriteKit som du vill simulera fysik på något sätt måste tilldelas en unik SKPhysicsBody objekt. Fysik organ innehåller flera egenskaper inklusive:

  • massa
  • densitet
  • område
  • friktion
  • hastighet

I dina spel kan du också definiera upp till 32 unika kategorier och en fysik kropp kan tilldelas till något antal av dessa kategorier. Kategorier är mycket användbara för att bestämma vilka noder i din scen som kan interagera med varandra när det gäller kollisioner.

På fysikorganen är dessa kategorier representerade av categoryBitMask och collisionBitMask egenskaper som båda ges med 0xFFFFFFFF värdet som standard. Det innebär att alla noder tillhör alla kategorier. Det är viktigt att notera att i varje värde varje hexadecimal siffra F är en shorthandform och representerar numret 15 i binära siffror (1111) som var och en motsvarar en av de 32 kategorier du kan använda.

När två noder kolliderar, en logisk OCH operation utförs på collisionBitMask och den categoryBitMask av den första respektive andra kroppen. Om resultatet är ett värde som inte är noll, utför SpriteKit sin simulering på de två noder som kropparna tillhör.

Observera att detta OCH beräkningen utförs två gånger med de två organen byttes runt. Till exempel:

  • Beräkning 1: bodyA.collisionBitMask & bodyB.categoryBitMask
  • Beräkning 2: bodyB.collisionBitMask & bodyA.categoryBitMask

Om du inte vet hur en OCH operatörsarbeten, så här är ett mycket enkelt exempel skrivet i Swift:

låt mask1 = 0x000000FF låt mask2 = 0x000000F0 låt resultat = mask1 & mask2 // resultat = 0x000000F0

De OCH operatören bestämmer vilka delar av bitmaskerna som är samma och returnerar ett nytt bitmaskeringsvärde som innehåller matchande delar.

En viktig sak att notera är att dessa bitmasker endast påverkar SpriteKits sida av fysiksimuleringen och du får inte meddelas om kollisioner som detekterats på detta sätt. Det betyder att organ kan interagera med varandra men ingen av kontaktdelegatets metoder kallas.

För att dessa metoder ska kunna utföras måste du ange a contactTestBitMask för varje kropp, som producerar ett icke-nollvärde när en OCH operatören agerar på dem. För alla fysikorgan har denna bitmask ett standardvärde av 0x00000000 vilket betyder att du inte kommer att bli underrättad om några kollisioner som fysikorganet deltar i.

Fysikorgan, inklusive deras olika bitmaskar, kan ställas in i Xcode scenredigeraren. Öppna MainScene.sks, välj bilen och öppna Attribut Inspector till höger. Bläddra ner till Fysik Definition sektion.

Därför att Kroppstyp är satt till Ingen, ingen av egenskaperna relaterade till fysik är synliga. För att ändra detta måste vi ställa in Kroppstyp till ett annat värde än Ingen. Tre kroppstyper finns tillgängliga:

  • begränsande rektangel
  • begränsande cirkel
  • alfa mask
th

Dessa tre fysik kroppstyper är de vanligaste i SpriteKit. Bindande rektangel och Bindande cirkel arbeta genom att skapa en barriär runt sprite som ska användas i fysik simuleringar. Detta innebär att sprite kolliderar med en annan nod när dess avgränsande form träffar en annan nods fysik kropp.

Kanten på en avgränsande rektangel är exakt densamma som nodens storlek som visas i scenredigeraren. Om du väljer Bindande cirkel, Men du ser en tunn ljusblå cirkel som representerar formen på gränsen.

Alpha mask fungerar lite annorlunda och tittar på spritens faktiska bildtextur för att bestämma fysikens kropps kanter. Denna kroppstyp är överlägset mest exakt i SpriteKit, men det kan ha stor inverkan på spelets prestanda, speciellt när man använder sprites med komplexa former.

För vårt spel, eftersom vi bara använder en bilsprite och vår scen inte är särskilt komplex, kommer vi att använda Alpha mask kroppstyp. Det är inte rekommenderas att använda denna kroppstyp för alla sprites i din scen trots att den är mest exakt. När du väljer det här alternativet från rullgardinsmenyn bör du se en ljusblå linje visas runt kanten av bilen.

Det är viktigt att notera att andra typer av fysikorgan kan skapas programmatiskt, såsom organ från CGPath objekt samt cirklar och rektanglar av anpassade storlekar.

Fortfarande i Attribut Inspector, Du borde nu se fler alternativ tillgängliga för dig i Fysik Definition sektion. Den enda egenskapen vi behöver ändra är Kontakta Mask. Ändra detta till ett värde av 1.

Med bilens fysik kropp upp, kan vi börja lägga några hinder i spelet för att kollidera med bilen.

3. Upptäcka kollisioner

Innan vi implementerar metoderna i SKPhysicsContactDelegate protokoll måste vi lägga till några hinder för att bilen ska undvikas. För att göra detta ska vi krossa ett nytt hinder var tredje sekund framför bilen och placera hindret i en slumpmässig lane.

Öppna MainScene.swift och lägg till ett importdeklaration för GameplayKit ram så att vi kan använda slumptalsgeneratorerna som tillhandahålls av GameplayKit.

importera GameplayKit

Lägg sedan till följande metod i MainScene klass:

func spawnObstacle (timer: NSTimer) om player.hidden timer.invalidate () return låt spriteGenerator = GKShuffledDistribution (lowestValue: 1, highestValue: 2) låt hinder = SKSpriteNode (imageNamed: "Obstacle \ (spriteGenerator.nextInt ") obstacle.xScale = 0.3 obstacle.yScale = 0.3 låt physicsBody = SKPhysicsBody (circleOfRadius: 15) physicsBody.contactTestBitMask = 0x00000001 physicsBody.pinned = true physicsBody.allowsRotation = falskt hinder.physicsBody = physicsBody let center = size.width / 2.0, skillnad = CGFloat (85.0) var x: CGFloat = 0 låtGenerator = GKShuffledDistribution (lowestValue: 1, highestValue: 3) switch laneGenerator.nextInt () fall 1: x = center - skillnad fall 2: x = center case 3: x = centrum + skillnadsstandard: fatalError ("Antal utanför [1, 3] genererade") obstacle.position = CGPoint (x: x, y: (player.position.y + 800)) addChild (hinder)

Denna metod påkallas var tredje sekund. Låt oss bryta ner det för att se vad som händer. Om den nuvarande spelarnoden är dold, vilken är sann när bilen träffar ett hinder, så upphäver vi timern och stoppar göshinder.

Vi får ett slumptal mellan 1 och 2, och använd detta för att skapa en sprite nod med ett av de två hinder som finns tillgängliga i projektet. Vi ändrar sedan hinderets skala så att de är tillräckligt små för att manövrera bilen.

Därefter skapar vi en fysik kropp för detta nya hinder med en cirkel med en radie av 15 och en kontaktmask av 0x00000001. Vi sätter nålas till Sann och allowsRotation till falsk så att hindret stannar på plats och inte rör sig. Den fysiska kroppen är sedan tilldelad hindret.

Vi genererar ett annat slumptal mellan 1 och 3 för att bestämma vilken lane hindret ska placeras i och ge hindret sin beräknade position och lägga den till scenen.

Observera att den horisontella skillnaden, 85, Använd i spawnObstacle (_ :) skiljer sig från den som användes vid flyttning av bilen, 70. Vi gör detta för att ge lite mer plats för bilen att flytta mellan hinder.

Med hindergawningslogiken på plats kan vi lägga till följande kod till slutet av didMoveToView (_ :) metod.

åsidosätta func didMoveToView (visa: SKView) ... låt timer = NSTimer (timeInterval: 3.0, mål: self, selector: #selector (spawnInObstacle (_ :)), userInfo: nil, repeats: true) NSRunLoop.mainRunLoop (). addTimer (timer, forMode: NSRunLoopCommonModes) låt kamera = SKCameraNode () self.camera = kamerakamera.position = CGPoint (x: center, y: player.position.y + 200) låt moveForward = SKAction.moveBy (CGVectorMake (0, 100 ), duration: 1.0) camera.runAction (SKAction.repeatActionForever (moveForward)) addChild (kamera) player.xScale = 0.4; player.yScale = 0.4 // Gör bilen mindre för att passa bättre mellan hinder

Vi skapar en timer för att utföra spawnObstacle (_ :) var tredje sekund och lägg den till huvudlössen. Vi skapar också en SKCameraNode att fungera som kameran för scenen och tilldela den till kamera egenskapen hos scenen. Detta gör att scenen återges från den här kamerans nods synvinkel. Observera att kameran är centrerad horisontellt och något ovanför bilen.

Vi lägger också till en identisk åtgärd för kameran så att den hålls uppradad med bilen. Vi lägger till kameran som en barnnod på scenen som vilken annan vanlig nod som helst. Sist men inte minst, vi skala ner bilen så att den kan passa mellan hindren lite bättre.

Den sista delen för kollisionsdetektering är att implementera endera av SKPhysicsContactDelegate protokollmetoder. I vårt fall ska vi genomföra didBeginContact (_ :) metod som vi vill bli underrättad så snart bilen träffar ett hinder. Lägg till följande metod för MainScene klass.

func didBeginContact (kontakt: SKPhysicsContact) om contact.bodyA.node == player || contact.bodyB.node == player player.hidden = true player.removeAllActions () kamera? .removeAllActions ()

Du kan se att den här metoden har en SKPhysicsContact parametern passerade till den. Detta objekt innehåller viktig information om kollisionen, inklusive dess riktning, impuls och de berörda objekten.

I denna kod är vi bara oroade över vilka noder som är inblandade i kollisionen. Vi kontrollerar om någon av dem var bilen och, om det är sant, gömmer bilen i scenen och avslutar bilens och kamerans rörelse.

Bygg och kör din app och spela ditt spel. Du kommer att se att när du hamnar i ett hinder försvinner bilen och scenen slutar röra sig.

Slutsats

Du borde nu veta hur man sätter upp fysikorgan för noderna i din scen och använder dessa för att simulera realistisk fysik. Du bör också vara bekväm att ställa in kollisionsdetektering i din scen genom att definiera en kontaktdelegator och tilldela bitmask för kontakttest för de noder du förväntar dig att kollidera med varandra.

I nästa handledning av SpriteKit From Scratch ska vi titta på den mer avancerade visuella funktionaliteten i SpriteKit, inklusive partikelsystem, ljus och filter.

Som alltid, var noga med att lämna dina kommentarer och feedback i kommentarerna nedan.