Construiți un joc Caterpillar cu Cocos2D detectarea coliziunilor

Aceasta este cea de-a șasea tranșă a seriei noastre de tutorial Cocos2D privind clonarea Centipede pentru iOS. Asigurați-vă că ați finalizat piesele anterioare înainte de a începe.


Ultima data…

În ultimul tutorial, v-am arătat cum să creați o serie de obiecte de rachete și să trageți un flux constant de ele. De asemenea, ați învățat despre interacțiunea cu jucătorii de bază în Cocos2D pentru a muta jucătorul.

În tutorialul de astăzi, vom explora cum să înființeze de bază coliziune în Cocos2D. Deși acest lucru nu este întotdeauna soluția optimă, este cu siguranță cel mai rapid și mai ușor de implementat.


Pasul 1: Coliziunea cu rachete / sproge

Coliziunea cu rachete este la fel ca și coliziunea jucătorului cu mugurii. Pur și simplu verificăm limitele fiecărei rachete în joc împotriva limitelor fiecărui germinat în joc și determinăm dacă se ciocnesc. Când un vițel a fost lovit, noi îi decrementăm viața, modificăm opacitatea și îl înlăturăm dacă viața ajunge la 0.

Deschideți Missile.m, importați Sprout.h și adăugați următorul cod în partea de jos a metodei de actualizare:

 CGRect missileRect = [auto getBounds]; [auto.gameLayer.sprouts enumerateObjectsUsingBlock: ^ (id id, NSUInteger idx, BOOL * oprire) Sprout * sprout = (Sprout *) obj; CGRect sproutRect = [sprout getBounds]; dacă (CGRectIntersectsRect (rachetăRect, sproutRect)) self.dirty = YES; sprout.lives--; ];

Pe masura ce fiecare racheta se actualizeaza, ea enumera toate varza in joc, verificand pentru a vedea daca rectul lor de intersectie se intersecteaza. Dacă există o coliziune, am stabilit racheta la "murdar" și diminuează viața germinei în consecință. Am scris deja codul pentru a modifica opacitatea sprouturilor pe baza vieții sale, deci, dacă executați jocul în acest moment, ar trebui să vedeți că mugurii se schimbă atunci când sunt loviți. Problema este că ei încă se află în joc. Trebuie să eliminăm mugurii cu 0 vieți.

Acest lucru se poate face în interiorul Actualizați: în GameLayer.m. Deschideți GameLayer.m și adăugați următorul cod în partea de jos a Actualizați: metodă:

 // 1 __block Sprout * deadSprout = zero; [auto.sprouts enumerateObjectsUsingBlock: ^ (id obj, NSUInteger idx, BOOL * oprire) Sprout * sprout = (Sprout *) obj; dacă (sprout.lives == 0) deadSprout = sprout; * oprire = DA; ]; // 2 în cazul în care (deadSprout) [auto.spritesBatchNode removeChild: deadSprout.sprite cleanup: YES]; [auto.sprouts removeObject: deadSprout]; 
  1. Enumerăm fiecare dintre puieții care caută un mort. Pentru a simplifica lucrurile, ștergem numai câte un fir pe iterație.
  2. Dacă există vâlvă moartă, eliminăm sprite-ul din nodul lotului și eliminăm vărsământul din matricea de lăstari.

Acum, conduceți jocul și veți vedea că mugurii se estompează atunci când sunt loviți și în cele din urmă dispar.


Pasul 2: Coliziunea Caterpillar

Acum trebuie să adăugăm detectarea coliziunii între rachetă și omidă. Această interacțiune este ceea ce face cu adevărat aplicația dvs. un joc. Odată lovită, omida ar trebui să se împartă la segmentul de lovituri și fiecare nouă "omidă" ar trebui să călătorească în direcții separate. Segmentul care a fost lovit apoi devine transformat într-un sprout.

Începeți prin deschiderea Missile.m, importând Caterpillar.h și Segment.h și adăugați următorul cod în partea de jos a paginii Actualizați: metodă:

 __block Caterpillar * hitCaterpillar = zero; __block Segment * hitSegment = zero; // 1 [self.gameLayer.caterpillars enumerateObjectsUsingBlock: ^ (id id, NSUInteger idx, BOOL * stop) Caterpillar * caterpillar = (Caterpillar *) obj; [caterpillar.segments enumerateObjectsUsingBlock: ^ (id obj, NSUInteger idx, BOOL * oprire) Segment * segment = (Segment *) obj; Segmentul CGRectRect = [segment getBounds]; // 2 dacă (CGRectIntersectsRect (rachetăRect, segmentRect)) auto.dirty = DA; hitCaterpillar = [păstrează unitatea]; hitSegment = [reține segmentul]; * oprire = DA; ]; ]; // 3 dacă (hitCaterpillar && hitSegment) [self.gameLayer splitCaterpillar: hitCaterpillar atSegment: hitSegment]; [release release hit]; [lansarea hitCaterpillar]; 
  1. Enumerați toate omizi în joc și enumerați fiecare dintre segmentele lor.
  2. Verificați o coliziune și amintiți-vă ce segment a fost omorât.
  3. Dacă avem o lovitură, am împărțit omida în segmentul actual. Nu vă faceți griji, vom pune în aplicare această metodă în scurt timp.

Acum, când avem rachete care se ciocnesc cu omida, există câteva lucruri pe care trebuie să le facem. Primul este de a scrie logica pentru a împărți osoasa pe un anumit segment. Acest lucru se va face în GameLayer.m. Mai întâi, deschideți GameLayer.h și adăugați următoarele declarații de clasă înainte.

 @ class Caterpillar; secțiunea @class;

Apoi, declarați următoarea metodă:

 - (void) splitCaterpillar: (Caterpillar *) caterpillar atSegment: (Segment *) segment;

Înainte de a începe implementarea, trebuie să adăugăm două fișiere la proiect. Descărcați NSArray + Reverse, dezarhivează-l și trageți ambele fișiere în proiectul dvs. Este pur și simplu o categorie pe NSMutableArray care ne oferă o metodă inversă. Acum, deschideți GameLayer.m, importați Segment.h și NSArray + Reverse.h și implementați următoarea metodă:

 - (void) splitCaterpillar: (Caterpillar *) caterpillar atSegment: (Segment *) segment // 1 dacă ([caterpillar.segments count] == ​​1) [auto.spritesBatchNode removeChild: segment.sprite cleanup: NO]; [auto.caterpillars removeObject: caterpillar]; [self createSproutAtPostion: segment.position]; întoarcere;  // 2 [auto.spritesBatchNode removeChild: segment.sprite cleanup: NO]; // 3 [self createSproutAtPostion: segment.position]; // 4 NSInteger indexOfSegement = [caterpillar.segments indexOfObject: segment]; NSMutableArray * headSegments = [NSMutableArray matrice]; NSMutableArray * tailsSegments = [șir NSMutableArray]; // 5 [caterpillar.segments enumerateObjectsUsingBlock: ^ (id id, NSUInteger idx, BOOL * stop) if (idx < indexOfSegement)  [headSegments addObject:obj];  else if(idx > indexOfSegement) [coziSegments addObject: obj]; ]; // 6 dacă ([tail Segments count]> 0) // 7 [tail Segments reverse]; // 8 Caterpillar * newCaterpillar = [[Caterpillar alloc] initWithGameLayer: segmente de segmente: coziSegmente de nivel: self.level] autorelease]; newCaterpillar.position = [[tailsSegments objectAtIndex: 0] poziție]; // 9 dacă (caterpillar.currentState == CSRight || caterpillar.previousState == CSRight) // A fost îndreptată spre dreapta dacă (caterpillar.currentState == CSDownLeft || caterpillar.currentState == CSDownRight) // Se îndreaptă în josul newCaterpillar .previousState = CSUpRight;  altceva // Se îndreaptă spre newCaterpillar.previousState = CSDownRight;  newCaterpillar.currentState = CSLeft;  altceva // A fost îndreptată spre stânga dacă (caterpillar.currentState == CSDownLeft || caterpillar.currentState == CSDownRight) // Se îndreaptă în jos newCaterpillar.previousState = CSUpRight;  altceva // Se îndreaptă spre newCaterpillar.previousState = CSDownRight;  newCaterpillar.currentState = CSRight;  [auto.caterpilluri addObject: newCaterpillar];  // 10 dacă ([numărul segmentelor segmente]> 0) caterpillar.segments = headSegments;  altfel [self.caterpillars removeObject: caterpillar]; 
  1. Dacă am lovit un singur segment de omidă (doar un cap), vom elimina acea omidă din joc și o vom converti într-un germen.
  2. Eliminați sprite-ul segmentului care a fost lovit de la nodul lotului.
  3. Transformați segmentul de lovituri la un vițel (vom implementa această metodă momentan).
  4. Trebuie să împărțim omida în două tablouri, secțiunea capului și secțiunea coadă.
  5. Determinați unde se încadrează celelalte segmente (secțiunea cap sau coadă) și adăugați-le în matricele corespunzătoare.
  6. Verificați dacă există segmente de coadă.
  7. Reveniți la matricea segmentelor coadă. Aceasta este o categorie pe NSMutableArray care a fost menționată mai sus. Trebuie să inversăm segmentele pentru a mișca omida în direcția opusă.
  8. Creați un obiect omidă nou utilizând secțiunea cozii. Vom implementa această metodă momentan.
  9. Aceasta este curajul acestei metode. Aici determinăm direcția curentă (stânga sau dreapta) și direcția generală curentă (în sus sau în jos) a omizi care a fost lovită pentru a trimite noua omidă în direcția opusă.
  10. Dacă mai rămâne un cap, am stabilit doar șoferul care a fost lovit de segmente la celelalte segmente ale capului.

Wow, a fost o mulțime de luat. Mai ești cu mine? Există câteva metode pe care le-am folosit aici și în codul anterior, care trebuie încă implementate. Prima metodă de implementare este createSproutAtPosition:. Adăugați următoarea declarație de metodă la interfața dvs. privată din partea de sus a GameLayer.m:

 - (Void) createSproutAtPostion: poziția (CGPoint);

Acum, implementați următoarea metodă:

 - (void) createSproutAtPostion: poziția (CGPoint) // 1 int x = (position.x - kGameAreaStartX) / kGridCellSize; int y = (kGameAreaStartY - kGridCellSize + kGameAreaHeight + kGridCellSize / 2 - poziția.y) / kGridCellSize; // 2 Sprout * sprout = [[Sprout alinia] initWithGameLayer: self]; sprout.position = poziție; [auto.sprouts addObject: sprout]; _locații [x] [y] = DA; 
  1. Aceasta ne traduce poziția pe coordonatele ecranului la coordonatele rețelei. Folosind abilitatea noastră de algebră din clasa a VIII-a, rezultă din codul de poziționare scris în partea a doua.
  2. Creați un nou germinat și adăugați-l la joc.

Ultima metodă pe care trebuie să o implementăm pentru a face acest lucru este de lucru initWithGameLayer: segmente: Nivel: în clasa omidă. Această metodă va fi responsabilă pentru construirea unei noi oglinzi din segmentele care au fost transmise. Deschideți Caterpillar.h și adăugați următoarea declarație de metodă:

 - (id) initWithGameLayer: (GameLayer *) segmente de strat: (NSMutableArray *) nivel de segmente: (NSInteger);

Acum, deschideți Caterpillar.m și implementați următoarea metodă:

 - (id) = initWithGameLayer: (GameLayer *) segmente de strat: (NSMutableArray *) segmente nivel: (NSInteger) nivel if (auto = [super initWithGameLayer: layer]) self.segments = segmente; self.level = nivel; self.currentState = CSRight; auto.previousState = CSDownLeft; // setați poziția celorlalte segmente __block int x = 0; __block Segment * parentSegment = [auto.segmentul obiectAtIndex: 0]; parentSegment.parent = zero; [segmente de sine enumerateObjectsUsingBlock: ^ (id obj, NSUInteger idx, BOOL * oprire) Segment * segment = (Segment *) obj; dacă (x ++> 0) if (! [segmentul esteEqual: parentSegment]) segment.parent = părinteSegment;  parentSegment = segment; ];  întoarce-te; 

Această metodă este aproape identică cu cea anterioară initWithGameLayer: Nivel: Poziția: metodă. Singura diferență este mai degrabă decât alocarea unei serii de segmente, setează matricea segmentelor la segmentele de intrare care sunt transmise.

Mergeți și conduceți jocul în acest stadiu. Ar trebui să fii capabil să omori pe deplin omida care se află în joc.


Pasul 3: Coliziunea Caterpillar a jucătorului

Ultimul lucru pe care avem nevoie pentru a finaliza cercul de detecție a coliziunii vieții este implementarea coliziunilor dintre omidă și player. Dacă o parte a oaselor lovește jucătorul, vrem să reducem numărul de vieți ale jucătorului. În plus, jucătorul va deveni invincibil pentru un moment scurt, astfel încât omida nu numai că se aprinde prin el.

Începeți prin a deschide GameConfig.h și adăugați următoarea opțiune:

 #define kPlayerInvincibleTime 15

Acum, deschideți Caterpillar.m, introduceți Player.h și adăugați următorul cod în partea de jos a ferestrei Actualizați: înainte de a seta poziția omisei:

 player static intInvincibleCount = kPlayerInvincibleTime; static BOOL playerHit; CGRect playerRect = [auto.gameLayer.player getBounds]; // 1 [segmente de sine enumerateObjectsUsingBlock: ^ (id obj, NSUInteger idx, BOOL * oprire) Segment * segment = (Segment *) obj; Segmentul CGRectRect = [segment getBounds]; dacă (CGRectIntersectsRect (segmentRect, playerRect) && playerInvincibleCount == kPlayerInvincibleTime) * stop = DA; playerHit = DA; // 2 self.gameLayer.player.lives--; [[NSNotificationCenter defaultCenter] postNotificationName: kNotificationPlayerObiectivul obiect: nil]; ]; // 3 dacă (playerHit) if (playerInvincibleCount> 0) playerInvincibleCount--;  altfel playerHit = NU; playerInvincibleCount = kPlayerInvincibleTime; 
  1. Enumerați fiecare dintre segmente pentru a determina dacă acestea se ciocnesc cu playerul.
  2. Dacă jucătorul a fost lovit, scade viața și posta notificarea. Acest ar trebui să să se reflecte automat în interfața de utilizare pe baza codului pe care l-ați scris în partea a treia.
  3. Dacă jucătorul a fost lovit, verificați dacă acestea se află încă în perioada invincibilă. Dacă nu, resetați contorul invincibil astfel încât să poată fi lovit din nou.

Acum, conduceți jocul și lăsați-l pe omidă să vă lovească. Ar trebui să elimine una din viețile dvs. din partea de sus a ecranului.


Concluzie

Detectarea coliziunilor nu este niciodată o sarcină ușoară și am zgâriat doar suprafața. Dacă doriți să explorați mai profund detectarea coliziunilor, aruncați o privire la utilizarea Box2D cu Cocos2D.


Data viitoare

În următorul tutorial al seriei, vom discuta despre jocul de lustruire. Aceasta va include condițiile câștigătoare, scorul, sunetele, jocul și scorul mare.

Codificare fericită!

Cod