SpriteKit From Scratch Fysikk og Kollisjoner

Introduksjon

I denne opplæringen, den tredje delen av SpriteKit From Scratch-serien, tar vi et detaljert innblikk i fysikk simuleringsfunksjonaliteten til SpriteKit og hvordan dette kan utnyttes i 2D SpriteKit-spillene dine.

Denne opplæringen krever at du kjører Xcode 7.3 eller høyere, som inkluderer Swift 2.2 og iOS 9.3, tvOS 9.2 og OS X 10.11.4 SDKs.

For å følge med, kan du enten bruke prosjektet du opprettet i den forrige opplæringen eller laste ned en ny kopi fra GitHub.

Grafikken som brukes til spillet i denne serien, finnes på GraphicRiver. GraphicRiver er en god kilde for å finne kunst og grafikk for spillene dine.

1. Fysikk Verdener

Den første delen av en fysikk simulering i SpriteKit er physicsWorld eiendom av gjeldende scene. Denne eiendommen er en SKPhysicsWorld objekt som definerer egenskaper som din scene er tyngdekraft, fysikk simuleringshastighet og kontaktperson. Fysikkverdener kan også definere ledd mellom objekter for å effektivt knytte flere noder sammen på bestemte punkter.

Tyngde

For toppvisning stilspill vi lager i denne serien, ønsker vi å endre standardgravitasjonsverdien fra SpriteKit. Standardgravitasjonen er beregnet for a forfra spill med en verdi på (0, -9,8) som simulerer jordens tyngdekraft, det vil si, 0 horisontal akselerasjon og en nedadgående akselerasjon av 9.8m / s². For vårt spill trenger vi 0 vertikal tyngdekraft slik at bilen ikke begynner å akselerere nedover når vi setter dens fysikkegenskaper.

Åpen MainScene.sks og klikk på den grå bakgrunnen for å velge scene. Deretter åpner du Attributtsinspektør og endre Tyngde slik at begge X og komponenter er satt til 0.

Det er viktig å merke seg at tyngdekraften er den eneste fysikkverdenen som kan endres ved hjelp av Xcodes sceneredaktør. Eventuelle andre egenskaper må endres programmatisk.

Kontakt Delegate

For at spillet skal kunne oppdage kollisjoner mellom objekter, må vi sette scenens fysikkverden contactDelegate eiendom. Denne delegaten kan være ethvert objekt som samsvarer med SKPhysicsContactDelegate protokoll. Denne protokollen definerer to metoder, didBeginContact (_ :) og didEndContact (_ :). Du kan bruke disse metodene til å utføre handlinger basert på objekter som kolliderer i scenen.

For å holde vår kode sammen, skal vi lage MainScene eksempelvis sin egen kontaktdelegate. Åpen MainScene.swift og rediger MainScene klasse definisjon for å passe til SKPhysicsContactDelegate protokollen.

importere UIKit import SpriteKit klasse MainScene: SKScene, SKPhysicsContactDelegate ...

didMoveToView (_ :), vi satte MainScene eksempel som kontaktdelegator for physicsWorld eiendom.

overstyr func didMoveToView (se: SKView) ... physicsWorld.contactDelegate = self

Vi vil implementere metodene til SKPhysicsContactDelegate protokollen senere. Vi må først sette opp fysikkegenskapene til noderne i scenen.

2. Fysikkorganer

Enhver node i SpriteKit som du vil simulere fysikk på på en måte må tildeles en unik SKPhysicsBody gjenstand. Fysikkorganer inneholder flere egenskaper, inkludert:

  • masse
  • tetthet
  • område
  • friksjon
  • hastighet

I spillene dine kan du også definere opptil 32 unike kategorier og en fysikk kropp kan tilordnes til et hvilket som helst antall av disse kategoriene. Kategorier er svært nyttige for å bestemme hvilke noder i din scene kan samhandle med hverandre når det gjelder kollisjoner.

På fysikkorganer er disse kategoriene representert av categoryBitMask og collisionBitMask egenskaper, som begge er gitt 0xFFFFFFFF verdi som standard. Dette betyr at alle noder tilhører alle kategorier. Det er viktig å merke seg at i denne verdien, hvert heksadesimale siffer F er en shorthandform og representerer tallet 15 i binære sifre (1111) som hver svarer til en av de 32 kategoriene du kan bruke.

Når to noder kolliderer, en logisk OG operasjonen utføres på collisionBitMask og categoryBitMask av henholdsvis den første og den andre kroppen. Hvis resultatet er en null-verdi, utfører SpriteKit sin simulering på de to noder kroppen tilhører.

Merk at dette OG beregning utføres to ganger med de to organene byttet rundt. For eksempel:

  • Beregning 1: bodyA.collisionBitMask & bodyB.categoryBitMask
  • Beregning 2: bodyB.collisionBitMask & bodyA.categoryBitMask

Hvis du ikke vet hvordan en OG operatør fungerer, så her er et veldig enkelt eksempel skrevet i Swift:

la mask1 = 0x000000FF la mask2 = 0x000000F0 la resultatet = mask1 & mask2 // resultat = 0x000000F0

De OG operatøren bestemmer hvilke deler av bitmasker som er de samme og returnerer en ny bitmaskeverdi som inneholder de matchende delene.

En viktig ting å merke seg er at disse bitmasker bare påvirker SpriteKits side av fysikk simuleringen, og du blir ikke varslet om kollisjoner oppdaget på denne måten. Dette betyr at organer kan samhandle med hverandre, men ingen av kontaktdelegatorens metoder kalles.

For at disse metodene skal utføres, må du angi en contactTestBitMask for hver kropp, som produserer en ikke-null verdi når en OG operatør fungerer på dem. For alle fysikklegemer har denne bitmasken en standardverdi på 0x00000000 noe som betyr at du ikke vil bli varslet om eventuelle kollisjoner som fysikkorganet deltar i.

Fysikkorganer, inkludert deres forskjellige bitmasker, kan settes opp i Xcode scene editor. Åpen MainScene.sks, velg bilen, og åpne Attributtsinspektør til høyre. Rull ned til Fysikk Definisjon seksjon.

Fordi Kroppstype er satt til Ingen, Ingen av egenskapene knyttet til fysikk er synlige. For å endre dette må vi sette Kroppstype til en annen verdi enn Ingen. Tre kroppstyper er tilgjengelige:

  • begrensende rektangel
  • begrensende sirkel
  • alfa maske
th

Disse tre fysikk kroppstyper er de vanligste i SpriteKit. Bundet rektangel og Bounding sirkel arbeid ved å skape en barriere rundt sprite som skal brukes i fysikk simuleringer. Dette betyr at sprite kolliderer med en annen knute når dens avgrensende form treffer en annen knutes fysikk kropp.

Kanten på et avgrensende rektangel er nøyaktig det samme som nodens størrelse vist i sceneditoren. Hvis du velger Bounding sirkel, Du ser imidlertid en tynn lyseblå sirkel som representerer formen på avgrensesirkelen.

Alfa maske virker litt annerledes og ser på den faktiske bildestrukturen til sprite for å bestemme fysikkens kropps kanter. Denne kroppstypen er den mest nøyaktige i SpriteKit, men det kan ha stor innvirkning på spillets ytelse, spesielt når du bruker sprites med komplekse former.

For vårt spill, siden vi bare bruker en bilsprite og vår scene ikke er spesielt kompleks, skal vi bruke Alfa maske Kroppstype. Det er ikke anbefales å bruke denne kroppstypen for alle sprites i scenen din selv om den er den mest nøyaktige. Når du velger dette alternativet fra rullegardinmenyen, bør du se en lyseblå linje vises rundt kanten av bilen.

Det er viktig å merke seg at andre typer fysikkorganer kan opprettes programmatisk, for eksempel organer fra CGPath objekter samt sirkler og rektangler av egendefinerte størrelser.

Fortsatt i Attributtsinspektør, Du bør nå se flere alternativer tilgjengelig for deg i Fysikk Definisjon seksjon. Den eneste eiendommen vi trenger å endre er Kontakt Mask. Endre dette til en verdi av 1.

Med bilens fysikk kropp satt opp, kan vi begynne å sette noen hindringer inn i spillet for å kollidere med bilen.

3. Påvisning av kollisjoner

Før vi implementerer metodene til SKPhysicsContactDelegate protokoll, må vi legge til noen hindringer for at bilen skal unngå. For å gjøre dette skal vi gyte et nytt hinder hvert tredje sekund foran bilen og plassere hinderet i en tilfeldig felt.

Åpen MainScene.swift og legg til en importerklæring for GameplayKit rammeverk slik at vi kan bruke tilfeldige tallgivere levert av GameplayKit.

importer GameplayKit

Deretter legger du til følgende metode for MainScene klasse:

func spawnObstacle (timer: NSTimer) hvis player.hidden timer.invalidate () return la spriteGenerator = GKShuffledDistribution (lowestValue: 1, highestValue: 2) la hindring = SKSpriteNode (imageNamed: "Hindring \ (spriteGenerator.nextInt ()) ") obstacle.xScale = 0.3 obstacle.yScale = 0.3 la physicsBody = SKPhysicsBody (circleOfRadius: 15) physicsBody.contactTestBitMask = 0x00000001 physicsBody.pinned = true physicsBody.allowsRotation = falsk hindring.physicsBody = physicsBody let center = size.width / 2.0, forskjell = CGFloat (85,0) var x: CGFloat = 0 la laneGenerator = GKShuffledDistribution (lowestValue: 1, highestValue: 3) bytt laneGenerator.nextInt () tilfelle 1: x = senterforskjell saks 2: x = senterhus 3: x = senter + differanse standard: fatalError ("Antall utenfor [1, 3] generert") obstacle.position = CGPoint (x: x, y: (player.position.y + 800)) addChild (hinder)

Denne metoden er påkalt hvert tredje sekund. La oss slå det ned for å se hva som skjer. Hvis gjeldende spillerens knutepunkt er skjult, hvilket er sant når bilen treffer et hinder, annullerer vi timeren og stopper gytehinder.

Vi får et tilfeldig tall mellom 1 og 2, og bruk dette til å lage en sprite node med en av de to hindringene sprites tilgjengelig i prosjektet. Vi endrer deretter hindringens skala slik at de er små nok til at bilen skal manøvrere rundt.

Deretter oppretter vi en fysikk kropp for dette nye hinderet med en sirkel med en radius av 15 og en kontaktmaske av 0x00000001. Vi setter låste til ekte og allowsRotation til falsk slik at hindringen forblir på plass og ikke beveger seg. Fysikklegemet er da tilordnet hindringen.

Vi genererer et annet tilfeldig tall mellom 1 og 3 for å avgjøre hvilken bane hindringen skal plasseres i og gi hindringen sin beregnede posisjon, legger den til scenen.

Legg merke til at den horisontale forskjellen, 85, brukt i spawnObstacle (_ :) er forskjellig fra den som brukes når du flytter bilen, 70. Vi gjør dette for å gi litt mer plass til bilen å bevege seg mellom hindringer.

Med hindringens gytlogikk på plass, kan vi legge til følgende kode til slutten av didMoveToView (_ :) metode.

overstyr func didMoveToView (visning: SKView) ... la timer = NSTimer (timeInterval: 3.0, mål: selvvalg: #selector (spawnInObstacle (_ :)), brukerInfo: null, gjentakelser: sant) NSRunLoop.mainRunLoop (). addTimer (timer, forMode: NSRunLoopCommonModes) la kamera = SKCameraNode () self.camera = kamera kamera.position = CGPoint (x: senter, y: player.position.y + 200) la moveForward = SKAction.moveBy (CGVectorMake (0, 100 ), varighet: 1.0) camera.runAction (SKAction.repeatActionForever (moveForward)) addChild (kamera) player.xScale = 0.4; player.yScale = 0.4 // Gjør bil mindre for å passe bedre mellom hindringer

Vi lager en timer for å utføre spawnObstacle (_ :) hvert tredje sekund og legg det til hovedløpsløyfen. Vi lager også en SKCameraNode å fungere som kameraet for scenen og tildele det til kamera eiendom av scenen. Dette fører til at scenen blir gjengitt fra denne kamera nodeens synspunkt. Merk at kameraet er sentrert horisontalt og litt over bilen.

Vi legger også til en identisk handling for kameraet slik at den forblir kantet opp med bilen. Vi legger til kameraet som en barnekode på scenen som alle andre vanlige noder. Sist men ikke minst, skaler vi bilen ned, slik at den kan passe mellom hindringene litt bedre.

Den siste delen for kollisjonsdeteksjon er å implementere en av de to SKPhysicsContactDelegate protokollmetoder. I vårt tilfelle skal vi implementere didBeginContact (_ :) Metode som vi vil bli varslet så snart bilen har et hinder. Legg til følgende metode for MainScene klasse.

Func didBeginContact (kontakt: SKPhysicsContact) hvis kontakt.bodyA.node == spiller || contact.bodyB.node == player player.hidden = true player.removeAllActions () kamera? .removeAllActions ()

Du kan se at denne metoden har en SKPhysicsContact parameteren passert til den. Dette objektet inneholder nøkkelinformasjon om kollisjonen, inkludert retning, impuls og objektene som er involvert.

I denne koden er vi bare bekymret for hvilke noder som er involvert i kollisjonen. Vi sjekker for å se om en av dem var bilen, og hvis det er sant, skjul bilen i scenen og avslutt bevegelsen til bilen og kameraet.

Bygg og kjør appen din og spill spillet ditt. Du vil se at når du kommer inn i et hinder, forsvinner bilen og scenen slutter å bevege seg.

Konklusjon

Du bør nå vite hvordan du skal sette opp fysikkorganer for noder i scenen din og bruke disse til å simulere realistisk fysikk. Du bør også være komfortabel med å sette opp kollisjonsdeteksjon i din scene ved å definere en kontaktdelegate og tilordne kontakt-testmasker for noder du forventer å kollidere med hverandre.

I neste veiledning av SpriteKit From Scratch skal vi se på den mer avanserte visuelle funksjonaliteten i SpriteKit, inkludert partikkelsystemer, lys og filtre.

Som alltid, vær sikker på å legge igjen dine kommentarer og tilbakemeldinger i kommentarene nedenfor.