SpriteKit from Scratch Physics and Collisions

Invoering

In deze tutorial, de derde aflevering van de SpriteKit From Scratch-serie, nemen we een gedetailleerd kijkje in de fysica-simulatiefunctionaliteit van SpriteKit en hoe dit kan worden gebruikt in uw 2D SpriteKit-spellen.

Deze tutorial vereist dat je Xcode 7.3 of hoger gebruikt, waaronder Swift 2.2 en de iOS 9.3, tvOS 9.2 en OS X 10.11.4 SDK's.

Om mee te gaan, kun je het project dat je in de vorige tutorial hebt gemaakt gebruiken of een nieuwe kopie van GitHub downloaden.

De afbeeldingen die voor de game in deze serie worden gebruikt, zijn te vinden op GraphicRiver. GraphicRiver is een geweldige bron voor het vinden van illustraties en afbeeldingen voor uw games.

1. Natuurwerelden

Het eerste deel van elke fysica-simulatie in SpriteKit is de Physics World eigendom van de huidige scène. Deze eigenschap is een SKPhysicsWorld object dat eigenschappen definieert zoals de zwaartekracht van je scène, snelheid van simulatie van de natuurkunde en contactafgevaardigde. Natuurwerelden kunnen ook verbindingen tussen objecten definiëren om meerdere knooppunten op specifieke punten effectief te verbinden.

Zwaartekracht

Voor de bovenaanzicht stijlspel dat we in deze serie maken, willen we de standaardzwaartekrachtwaarde van SpriteKit wijzigen. De standaardzwaartekracht is bedoeld voor een vooraanzicht spel met een waarde van (0, -9.8) die de zwaartekracht van de aarde simuleert, dat is, 0 horizontale versnelling en een neerwaartse versnelling van 9,8 m / s². Voor onze game hebben we nodig 0 verticale zwaartekracht zodat de auto niet naar beneden begint te accelereren als we zijn fysische eigenschappen instellen.

Open MainScene.sks en klik op de grijze achtergrond om de scène te selecteren. Open vervolgens de Kenmerken Inspector en veranderen Zwaartekracht zodat zowel de X en componenten zijn ingesteld op 0.

Het is belangrijk om op te merken dat de zwaartekracht de enige eigenschap is van de fysische wereld die kan worden veranderd met behulp van de scene-editor van Xcode. Alle andere eigenschappen moeten programmatisch worden gewijzigd.

Neem contact op met afgevaardigde

Om het spel botsingen tussen objecten te laten detecteren, moeten we de fysica-wereld van de scène instellen contactDelegate eigendom. Deze afgevaardigde kan elk object zijn dat voldoet aan de SKPhysicsContactDelegate protocol. Dit protocol definieert twee methoden, didBeginContact (_ :) en didEndContact (_ :). U kunt deze methoden gebruiken om acties uit te voeren op basis van de objecten die in de scène botsen.

Om onze code bij elkaar te houden, gaan we het maken MainScene bijvoorbeeld zijn eigen contactafgevaardigde. Open MainScene.swift en bewerk de MainScene klassendefinitie om te conformeren aan de SKPhysicsContactDelegate protocol.

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

In didMoveToView (_ :), we hebben de MainScene bijvoorbeeld als contactafgevaardigde van de Physics World eigendom.

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

We zullen de methoden van de SKPhysicsContactDelegate protocol later. We moeten eerst de fysische eigenschappen van de knooppunten in de scène instellen.

2. Fysieke lichamen

Elk knooppunt in SpriteKit waarvan je de fysica op een of andere manier wilt simuleren, moet een unieke worden toegewezen SKPhysicsBody voorwerp. Natuurkundige lichamen bevatten verschillende eigenschappen, waaronder:

  • massa-
  • dichtheid
  • Gebied
  • wrijving
  • snelheid

In uw games kunt u maximaal 32 unieke categorieën definiëren en een fysica-instantie kan aan elk aantal van deze categorieën worden toegewezen. Categorieën zijn erg handig om te bepalen welke knooppunten in uw scène met elkaar kunnen communiceren in termen van botsingen.

Op natuurkundige lichamen worden deze categorieën vertegenwoordigd door de categoryBitMask en collisionBitMask eigenschappen, die beide de 0xFFFFFFFF waarde standaard. Dit betekent dat alle knooppunten tot alle categorieën behoren. Het is belangrijk op te merken dat in deze waarde elk hexadecimaal cijfer voorkomt F is een steno-vorm en vertegenwoordigt het getal 15 in binaire cijfers (1111) die elk overeenkomen met een van de 32 categorieën die u kunt gebruiken.

Wanneer twee knooppunten botsen, een logische EN bewerking wordt uitgevoerd op de collisionBitMask en de categoryBitMask van respectievelijk het eerste en tweede lichaam. Als het resultaat een niet-nulwaarde is, voert SpriteKit zijn simulatie uit op de twee knooppunten waartoe de organen behoren.

Merk op dat dit EN berekening wordt tweemaal uitgevoerd met de twee lichamen omgewisseld. Bijvoorbeeld:

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

Als je niet weet hoe EN operator werkt, dan is hier een heel eenvoudig voorbeeld geschreven in Swift:

laat mask1 = 0x000000FF laat mask2 = 0x000000F0 laat resultaat = mask1 & mask2 // result = 0x000000F0

De EN operator bepaalt welke delen van de bitmaskers hetzelfde zijn en retourneert een nieuwe bitmaskerwaarde die de overeenkomende onderdelen bevat.

Een belangrijk ding om op te merken is dat deze bitmaskers alleen de kant van SpriteKit van de fysica-simulatie beïnvloeden en u niet op de hoogte wordt gebracht van botsingen die op deze manier worden gedetecteerd. Dit betekent dat lichamen met elkaar kunnen interageren, maar geen van de methoden van de contactafgevaardigde worden genoemd.

Om deze methoden uit te voeren, moet u een contactTestBitMask voor elke instantie, die een niet-nulwaarde oplevert als een EN operator handelt op hen. Voor alle fysicalichamen heeft dit bitmasker een standaardwaarde van 0x00000000 wat betekent dat u niet op de hoogte wordt gebracht van eventuele botsingen waaraan het fysica-orgaan deelneemt.

Fysieke lichamen, inclusief hun verschillende bitmaskers, kunnen worden ingesteld in de Xcode scèneditor. Open MainScene.sks, selecteer de auto en open de Kenmerken Inspector aan de rechterkant. Blader omlaag naar de Definitie van natuurkunde sectie.

Omdat Lichaamstype ingesteld op Geen, geen van de eigenschappen gerelateerd aan fysica is zichtbaar. Om dit te veranderen, moeten we instellen Lichaamstype naar een andere waarde dan Geen. Er zijn drie lichaamstypen beschikbaar:

  • begrenzende rechthoek
  • begrenzende cirkel
  • alfamasker
th

Deze drie fysica lichaamstypen zijn de meest voorkomende in SpriteKit. Begrenzende rechthoek en Begrensde cirkel werk door een barrière te creëren rond de sprite die moet worden gebruikt in natuurkundige simulaties. Dit betekent dat de sprite botst met een ander knooppunt wanneer de begrenzende vorm de fysica van een ander knooppunt raakt.

De rand van een begrenzingsrechthoek is exact hetzelfde als de grootte van het knooppunt dat wordt weergegeven in de scène-editor. Als u selecteert Begrensde cirkel, u ziet echter een dunne lichtblauwe cirkel die de vorm van de cirkel aangeeft.

Alpha-masker werkt een beetje anders en kijkt naar de werkelijke afbeeldingsstructuur van de sprite om de randen van het fysieke lichaam te bepalen. Dit lichaamstype is verreweg het meest nauwkeurig in SpriteKit, maar het kan een grote invloed hebben op de prestaties van je spel, met name bij gebruik van sprites met complexe vormen.

Voor onze game, omdat we slechts één autosprite gebruiken en onze scène niet bijzonder complex is, gaan we de Alpha-masker lichaamstype. Het is niet aanbevolen om dit lichaamstype te gebruiken voor alle sprites in je scène, ook al is dit het meest accuraat. Wanneer u deze optie selecteert in het vervolgkeuzemenu, ziet u een lichtblauwe lijn rond de rand van de auto.

Het is belangrijk op te merken dat andere typen fysica-instanties programmatisch kunnen worden gemaakt, zoals instanties van CGPath objecten evenals cirkels en rechthoeken van aangepaste formaten.

Nog steeds in de Kenmerken Inspector, je zou nu meer opties voor je moeten zien in de Definitie van natuurkunde sectie. De enige eigenschap die we moeten wijzigen is de Neem contact op met Mask. Wijzig dit in een waarde van 1.

Met het fysieke lichaam van de auto opgezet, kunnen we beginnen met het plaatsen van obstakels in het spel om in botsing te komen met de auto.

3. Opsporen van botsingen

Voordat we de methoden van de SKPhysicsContactDelegate protocol, we moeten enkele obstakels toevoegen om de auto te vermijden. Om dit te doen, gaan we om de drie seconden een nieuw obstakel voor de auto leggen en het obstakel in een willekeurige rijstrook positioneren.

Open MainScene.swift en voeg een importinstructie toe voor de GameplayKit zodat we de willekeurige nummergeneratoren van GameplayKit kunnen gebruiken.

import GameplayKit

Voeg vervolgens de volgende methode toe aan de MainScene klasse:

func spawnObstacle (timer: NSTimer) if player.hidden timer.invalidate () return laat spriteGenerator = GKShuffledDistribution (lowestValue: 1, highestValue: 2) laat obstacle = SKSpriteNode (imageNamed: "Obstacle \ (spriteGenerator.nextInt ()) ") obstacle.xScale = 0.3 obstacle.yScale = 0.3 let physicsBody = SKPhysicsBody (circleOfRadius: 15) physics Body.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 () case 1: x = center - verschil case 2: x = center case 3: x = center + difference default: fatalError ("Nummer buiten [1, 3] gegenereerd") obstacle.position = CGPoint (x: x, y: (player.position.y + 800)) addChild (obstakel)

Deze methode wordt elke drie seconden aangeroepen. Laten we het opsplitsen om te zien wat er aan de hand is. Als het huidige spelerknooppunt verborgen is, wat waar is wanneer de auto een obstakel raakt, dan maken we de timer ongeldig en stoppen met het afwerpen van obstakels.

We krijgen een willekeurig getal tussen 1 en 2, en gebruik dit om een ​​sprite-knooppunt te maken met een van de twee obstakelsprites die beschikbaar zijn in het project. Vervolgens veranderen we de schaal van het obstakel, zodat ze klein genoeg zijn voor de auto om rond te manoeuvreren.

Vervolgens creëren we een fysisch lichaam voor dit nieuwe obstakel met een cirkel met een straal van 15 en een contactmasker van 0x00000001. We gaan zitten gespeld naar waar en allowsRotation naar vals zodat het obstakel op zijn plaats blijft en niet beweegt. Het fysische lichaam wordt vervolgens toegewezen aan het obstakel.

We genereren nog een willekeurig willekeurig getal tussen 1 en 3 om te bepalen op welke rijstrook het obstakel moet worden geplaatst en geef het obstakel zijn berekende positie door het aan de scène toe te voegen.

Merk op dat het horizontale verschil, 85, gebruikt in spawnObstacle (_ :) is anders dan degene die werd gebruikt bij het verplaatsen van de auto, 70. We doen dit om ruimte voor de auto tussen obstakels mogelijk te maken.

Met de logica voor het blokkeren van obstakels kunnen we de volgende code toevoegen aan het einde van de didMoveToView (_ :) methode.

override func didMoveToView (weergave: SKView) ... let timer = NSTimer (timeInterval: 3.0, target: self, selector: #selector (spawnInObstacle (_ :)), userInfo: nil, herhalingen: true) NSRunLoop.mainRunLoop (). addTimer (timer, forMode: NSRunLoopCommonModes) laat camera = SKCameraNode () self.camera = camera camera.position = CGPoint (x: center, y: player.position.y + 200) laat moveForward = SKAction.moveBy (CGVectorMake (0, 100 ), duur: 1,0) camera.runAction (SKAction.repeatActionForever (moveForward)) addChild (camera) player.xScale = 0,4; player.yScale = 0.4 // Maakt de auto kleiner om beter tussen obstakels te passen

We maken een timer om uit te voeren spawnObstacle (_ :) elke drie seconden en voeg het toe aan de hoofdlooplus. We maken ook een SKCameraNode om op te treden als de camera voor de scène en deze toe te wijzen aan de camera eigendom van de scène. Hierdoor wordt de scène gerenderd vanuit het gezichtspunt van deze cameraknoop. Merk op dat de camera horizontaal en iets boven de auto gecentreerd is.

We voegen ook een identieke actie toe aan de camera zodat deze op één lijn blijft met de auto. We voegen de camera toe als een kindknooppunt van de scène, net als elk ander normaal knooppunt. Last but not least, we verkleinen de auto zodat deze een beetje beter tussen de obstakels kan passen.

Het laatste onderdeel voor detectie van botsingen is het implementeren van een van de SKPhysicsContactDelegate protocol methoden. In ons geval gaan we het didBeginContact (_ :) methode omdat we op de hoogte willen worden gesteld zodra de auto een obstakel raakt. Voeg de volgende methode toe aan de MainScene klasse.

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

Je kunt zien dat deze methode er een heeft SKPhysicsContact parameter doorgegeven. Dit object bevat belangrijke informatie over de botsing, inclusief de richting, de impuls en de betrokken objecten.

In deze code maken we ons alleen zorgen over welke knooppunten bij de botsing zijn betrokken. We controleren om te zien of een van hen de auto was en, indien waar, de auto in de scène verbergen en de beweging van de auto en de camera beëindigen.

Bouw en voer je app uit en speel je game. Je zult zien dat, wanneer je een obstakel tegenkomt, de auto verdwijnt en de scène stopt met bewegen.

Conclusie

Je zou nu moeten weten hoe je natuurkundige lichamen in te stellen voor de knooppunten in je scène en deze gebruiken om realistische fysica te simuleren. U moet ook comfortabel zijn bij het instellen van botsdetectie in uw scène door een contactafgevaardigde te definiëren en contacttestbitmaskers toe te wijzen voor de knooppunten waarvan u verwacht dat ze met elkaar botsen.

In de volgende tutorial van SpriteKit From Scratch gaan we kijken naar de meer geavanceerde visuele functionaliteit in SpriteKit, inclusief deeltjessystemen, lichten en filters.

Laat zoals altijd uw opmerkingen en feedback achter in de opmerkingen hieronder.