În acest tutorial, cea de-a treia tranșă a seriei SpriteKit From Scratch, analizăm detaliat funcția de simulare fizică a SpriteKit și modul în care aceasta poate fi utilizată în jocurile dvs. 2D SpriteKit.
Acest tutorial necesită să executați Xcode 7.3 sau o versiune ulterioară, care include Swift 2.2 și kiturile SDK pentru iOS 9.3, tvOS 9.2 și OS X 10.11.4.
Pentru a continua, puteți utiliza proiectul pe care l-ați creat în tutorialul anterior sau puteți descărca o copie nouă de la GitHub.
Grafica folosită pentru joc în această serie poate fi găsită pe GraphicRiver. GraphicRiver este o sursă excelentă pentru găsirea de ilustrații și grafică pentru jocurile dvs..
Prima parte a oricărei simulări fizice în SpriteKit este physicsWorld
proprietatea scenei actuale. Această proprietate este o SKPhysicsWorld
obiect care definește proprietățile, cum ar fi gravitatea scenei dvs., viteza simulării fizicii și delegatul de contact. Lumile fizicii pot defini, de asemenea, îmbinări între obiecte pentru a conecta eficient mai multe noduri la anumite puncte.
Pentru vedere de sus stilul pe care îl creăm în această serie, dorim să schimbăm valoarea gravitară prestabilită oferită de SpriteKit. Greutatea prestabilită este destinată a vedere din față joc cu o valoare de (0, -9,8) care simulează gravitația Pământului, 0 accelerația orizontală și o accelerație descendentă 9.8m / s². Pentru jocul nostru, avem nevoie 0 gravitația verticală, astfel încât mașina să nu înceapă să accelereze în jos după ce ne-am fixat proprietățile fizice.
Deschis MainScene.sks și faceți clic pe fundalul gri pentru a selecta scena. Apoi, deschideți Atribuții Inspector și schimbare Gravitatie astfel încât atât X și Y componentele sunt setate la 0.
Este important de observat că gravitatea este singura proprietate a lumii fizice care poate fi schimbată folosind editorul de scene al lui Xcode. Orice alte proprietăți trebuie modificate programat.
Pentru ca jocul să detecteze coliziuni între obiecte, trebuie să setăm lumea fizicii scena contactDelegate
proprietate. Acest delegat poate fi orice obiect care este în conformitate cu SKPhysicsContactDelegate
protocol. Acest protocol definește două metode, didBeginContact (_ :)
și didEndContact (_ :)
. Puteți utiliza aceste metode pentru a efectua acțiuni bazate pe obiectele care se ciocnesc în scenă.
Pentru a păstra codul împreună, o să facem MainScene
exemplu propriul delegat de contact. Deschis MainScene.swift și editați MainScene
pentru a se conforma definiției clasei SKPhysicsContactDelegate
protocol.
import Import UIKit Clasă SpriteKit MainScene: SKScene, SKPhysicsContactDelegate ...
În didMoveToView (_ :)
, am setat MainScene
în calitate de delegat de contact al physicsWorld
proprietate.
suprascrie func didMoveToView (vizualizare: SKView) ... physicsWorld.contactDelegate = auto
Vom implementa metodele SKPhysicsContactDelegate
mai târziu. Mai întâi trebuie să stabilim proprietățile fizice ale nodurilor din scenă.
Orice nod din SpriteKit pe care doriți să-l simulați într-un fel într-un mod fizic trebuie să fie atribuit unic SKPhysicsBody
obiect. Organismele fizice conțin mai multe proprietăți, printre care:
În jocurile dvs. puteți defini până la 32 de categorii unice și un corp de fizică poate fi atribuit oricărui număr din aceste categorii. Categoriile sunt foarte utile pentru a determina care noduri din scenă dvs. pot interacționa între ele în ceea ce privește coliziunile.
În cazul organismelor de fizică, aceste categorii sunt reprezentate de categoryBitMask
și collisionBitMask
proprietăți, care sunt date ambelor 0xFFFFFFFF valoare implicită. Aceasta înseamnă că toate nodurile aparțin tuturor categoriilor. Este important să rețineți că în această valoare, fiecare cifră hexazecimală F este o formă de stenografie și reprezintă numărul 15 în cifre binare (1111), fiecare dintre acestea corespund uneia dintre cele 32 de categorii pe care le puteți utiliza.
Când două noduri se ciocnesc, o logică ȘI
funcționarea este efectuată pe collisionBitMask
si categoryBitMask
al primului și celui de-al doilea corp, respectiv. Dacă rezultatul este o valoare diferită de zero, atunci SpriteKit își realizează simularea pe cele două noduri din care apar corpurile.
Rețineți că acest lucru ȘI
calculul este efectuat de două ori cu cele două corpuri schimbate. De exemplu:
bodyA.collisionBitMask & bodyB.categoryBitMask
bodyB.collisionBitMask & bodyA.categoryBitMask
Dacă nu știi cum ȘI
operatorul funcționează, atunci este un exemplu foarte simplu scris în Swift:
lasă mask1 = 0x000000FF să lase mask2 = 0x000000F0 let rezult = mask1 & mask2 // result = 0x000000F0
ȘI
operator determină ce părți ale măștilor de biți sunt aceleași și returnează o nouă valoare a mască biți care conține părțile potrivite.
Un lucru important este să rețineți că aceste măști de biți afectează doar partea SpriteKit a simulării fizicii și nu sunteți informat despre coliziuni detectate în acest mod. Aceasta înseamnă că organismele pot interacționa între ele, dar niciuna dintre metodele delegatului de contact nu este chemată.
Pentru ca aceste metode să fie executate, trebuie să specificați a contactTestBitMask
pentru fiecare organism, care produce o valoare nenulă atunci când o ȘI
operatorul acționează asupra acestora. Pentru toate corpurile de fizică, această mască de biți are o valoare implicită de 0x00000000 ceea ce înseamnă că nu veți fi informat despre eventualele coliziuni la care participă corpul fizicii.
Corpurile fizice, inclusiv diferitele lor măști de biți, pot fi configurate în editorul de scenă Xcode. Deschis MainScene.sks, selectați mașina și deschideți Atribuții Inspector pe dreapta. Derulați în jos până la Definiția fizicii secțiune.
pentru că Tipul corpului este setat sa Nici unul, nici una dintre proprietățile legate de fizică nu este vizibilă. Pentru a schimba acest lucru, trebuie să stabilim Tipul corpului la o valoare diferită de Nici unul. Există trei tipuri de corpuri:
Aceste trei tipuri de corp fizic sunt cele mai comune în SpriteKit. Cadru dreptunghiular și Cerc de legare lucrați prin crearea unei barieră în jurul spritelui care va fi folosită în simulările fizicii. Aceasta înseamnă că sprite se ciocnește cu un alt nod ori de câte ori forma sa de legare lovește corpul fizicii unui alt nod.
Marginea unui dreptunghi delimitat este exact aceeași cu mărimea nodului afișată în editorul de scenă. Dacă selectați Cerc de legare, totuși, vedeți un cerc subțire, albastru deschis, reprezentând forma cercului de margine.
Masca alfa funcționează puțin diferit și se uită la textura reală a imaginii sprite pentru a determina marginile corpului fizicii. Acest tip de corp este de departe cel mai precis în SpriteKit, dar poate avea un impact mare asupra performanței jocului dvs., în special atunci când folosiți sprite cu forme complexe.
Pentru jocul nostru, deoarece folosim doar un sprite auto și scena noastră nu este deosebit de complexă, vom folosi Masca alfa tip de corp. Este nu a recomandat să utilizați acest tip de corp pentru toate spritele din scena dvs., chiar dacă acesta este cel mai precis. Când selectați această opțiune din meniul derulant, ar trebui să vedeți o linie albastră deschisă pe marginea mașinii.
Este important de observat că alte tipuri de corpuri de fizică pot fi create în mod programatic, cum ar fi organismele din CGPath
obiecte, precum și cercuri și dreptunghiuri de dimensiuni personalizate.
Încă în Atribuții Inspector, ar trebui să vedeți acum mai multe opțiuni disponibile în dvs. Definiția fizicii secțiune. Singura proprietate pe care trebuie să o schimbăm este Contact Mască. Schimbați această valoare la 1.
Cu corpul fizicii mașinii înființat, putem începe să punem niște obstacole în joc pentru a intra în coliziune cu mașina.
Înainte de a implementa metodele SKPhysicsContactDelegate
protocol, trebuie să adăugăm unele obstacole pentru a evita mașina. Pentru a face acest lucru, vom face un nou obstacol la fiecare trei secunde in fata masinii si vom pozitiona obstacolul intr-o banda aleatorie.
Deschis MainScene.swift și adăugați o declarație de import pentru GameplayKit astfel încât să putem folosi generatoarele de numere aleatoare furnizate de GameplayKit.
import GameplayKit
Apoi, adăugați următoarea metodă la MainScene
clasă:
func spawnObstacle (timer: NSTimer) dacă player.hidden timer.invalidate () return permite spriteGenerator = GKShuffledDistribution (lowestValue: 1, highestValue: ") obstacle.xScale = 0.3 obstacle.yScale = 0.3 let fizicsBody = SKPhysicsBody (circleOfRadius: 15) fizicsBody.contactTestBitMask = 0x00000001 fizicsBody.pinned = adevărat fizicăBody.allowsRotation = false obstacle.physicsBody = fizicăBody let center = size.width / 2.0, Diferență = CGFloat (85.0) var x: CGFloat = 0 laneGenerator = GKShuffledDistribution (lowestValue: 1, highestValue: 3) switch laneGenerator.nextInt () case 1: x = = center + diferența implicită: fatalError ("Numărul în afara [1, 3] generat") obstacle.position = CGPoint (x: x, y:
Această metodă este invocată la fiecare trei secunde. Să-l rupem pentru a vedea ce se întâmplă. Dacă nodul curent al jucătorului este ascuns, ceea ce este adevărat atunci când mașina atinge un obstacol, invalidăm temporizatorul și oprim obstacolele de reproducere.
Obținem un număr aleator între 1 și 2, și folosiți acest lucru pentru a crea un nod sprite cu unul dintre cele două sprite de obstacole disponibile în proiect. Apoi vom schimba scara obstacolului astfel încât să fie suficient de mici pentru ca mașina să se poată manevra în jur.
Apoi, vom crea un corp de fizică pentru acest nou obstacol cu un cerc cu o rază de 15 și o măști de contact 0x00000001. Noi am stabilit fixate
la Adevărat
și allowsRotation
la fals
astfel încât obstacolul să rămână în poziție și să nu se miște. Corpul fizicii este apoi atribuit obstacolului.
Noi generăm un alt număr aleator între 1 și 3 pentru a determina pe care banda să se pună obstacolul și pentru a da obstacolului poziția sa calculată, adăugând-o la scenă.
Rețineți că diferența orizontală, 85, folosit in spawnObstacle (_ :)
este diferită de cea utilizată la deplasarea mașinii, 70. Facem acest lucru pentru a permite un spațiu mai mic pentru ca mașina să se deplaseze între obstacole.
Cu logica de reproducere a obstacolelor în loc, putem adăuga următorul cod la sfârșitul didMoveToView (_ :)
metodă.
override funcția didMoveToView (vizualizare: SKView) ... permite timer = NSTimer (timeInterval: 3.0, target: self, selector: #selector (spawnInObstacle (_ :)), userInfo: (timer, forMode: NSRunLoopCommonModes) lasa camera = SKCameraNode () auto.camera = camera_camera.position = CGPoint (x: centru, y: player.position.y + 200) let moveForward = SKAction.moveBy (CGVectorMake ), durata: 1.0) camera.runAction (SKAction.repeatActionForever (moveForward)) addChild (camera) player.xScale = 0.4; player.yScale = 0.4 // face masina mai mică pentru a se potrivi mai bine între obstacole
Creăm un cronometru pentru a executa spawnObstacle (_ :)
la fiecare trei secunde și adăugați-l la bucla principală. De asemenea, creăm un SKCameraNode
pentru a acționa ca camera pentru scenă și a le aloca aparat foto
proprietatea scenei. Acest lucru face ca scena să fie redată din punctul de vedere al acestui nod al camerei. Rețineți că aparatul foto este centrat orizontal și ușor deasupra mașinii.
De asemenea, adăugăm o acțiune identică camerei, astfel încât să rămână aliniată cu mașina. Adăugăm camera ca un nod copil al scenei ca orice alt nod obișnuit. Nu în ultimul rând, diminuăm masina astfel încât să se poată adapta între obstacole puțin mai bine.
Ultima parte pentru detectarea coliziunilor este implementarea uneia dintre cele două SKPhysicsContactDelegate
metode de protocol. În cazul nostru, vom implementa didBeginContact (_ :)
deoarece dorim să fim avertizați imediat ce mașina atinge un obstacol. Adăugați următoarea metodă la MainScene
clasă.
func didBeginContact (contact: SKPhysicsContact) if contact.bodyA.node == player || contact.bodyB.node == player player.hidden = adevărat player.removeAllActions () camera? .removeAllActions ()
Puteți vedea că această metodă are una SKPhysicsContact
parametru trecut la el. Acest obiect conține informații cheie despre coliziune, inclusiv direcția, impulsul și obiectele implicate.
În acest cod, suntem doar preocupați de care noduri sunt implicate în coliziune. Verificăm dacă unul dintre ele este mașina și, dacă este adevărat, ascunde mașina în scenă și termină mișcarea mașinii și a camerei.
Construiți și rulați aplicația și jucați-vă jocul. Veți vedea că atunci când vă confruntați cu un obstacol, mașina dispare și scena se oprește.
Ar trebui acum să știți cum să înființați organisme de fizică pentru nodurile din scenă și să le folosiți pentru a simula fizica realistă. Ar trebui să fiți confortabil, de asemenea, să stabiliți detectarea coliziunilor în scenă prin definirea unui delegat de contact și să alocați măști de testare a contactelor pentru nodurile pe care vă așteptați să le colizați.
În următorul tutorial al SpriteKit From Scratch, vom analiza funcționalitatea vizuală mai avansată din SpriteKit, inclusiv sistemele de particule, luminile și filtrele.
Ca întotdeauna, asigurați-vă că părăsiți comentariile și comentariile dvs. în comentariile de mai jos.