Questa è la sesta puntata della nostra serie di tutorial Cocos2D sulla clonazione di Centipede per iOS. Assicurati di aver completato le parti precedenti prima di iniziare.
Nell'ultimo tutorial, ti ho mostrato come creare una schiera di oggetti missilistici e farne un flusso costante. Hai anche imparato a conoscere l'interazione di base del giocatore in Cocos2D per spostare il giocatore.
Nel tutorial di oggi, esploreremo come impostare di base rilevamento delle collisioni in Cocos2D. Anche se questa non è sempre la soluzione ottimale, è sicuramente la più rapida e semplice da implementare.
La collisione missilistica con i germogli è molto simile alla collisione del giocatore con i germogli. Controlliamo semplicemente i limiti di ciascun missile in gioco contro i limiti di ogni germoglio in gioco e determiniamo se vi è collisione. Quando un germoglio è stato colpito, decrementiamo la sua vita, ne modificiamo l'opacità e lo rimuoviamo se la sua vita raggiunge 0.
Apri Missile.m, importa Sprout.h e aggiungi il seguente codice alla fine del metodo di aggiornamento:
CGRect missileRect = [self getBounds]; [self.gameLayer.sprouts enumerateObjectsUsingBlock: ^ (id obj, NSUInteger idx, BOOL * stop) Sprout * sprout = (Sprout *) obj; CGRect sproutRect = [sprout getBounds]; if (CGRectIntersectsRect (missileRect, sproutRect)) self.dirty = YES; sprout.lives--; ];
Man mano che ogni missile si aggiorna, enumera tutti i germogli in gioco, controllando se i loro reces di delimitazione si intersecano. Se c'è una collisione, impostiamo il missile su "sporco" e diminuiamo di conseguenza le vite del germoglio. Abbiamo già scritto il codice per modificare l'opacità dei germogli in base alle sue vite, quindi se esegui il gioco a questo punto, dovresti vedere i germogli che cambiano quando vengono colpiti. Il problema è che sono ancora in gioco. Dobbiamo rimuovere i germogli con 0 vite.
Questo può essere fatto all'interno del aggiornare:
metodo in GameLayer.m. Apri GameLayer.m e aggiungi il seguente codice in fondo al file aggiornare:
metodo:
// 1 __block Sprout * deadSprout = nil; [self.sprouts enumerateObjectsUsingBlock: ^ (id obj, NSUInteger idx, BOOL * stop) Sprout * sprout = (Sprout *) obj; if (sprout.lives == 0) deadSprout = sprout; * stop = SÌ; ]; // 2 if (deadSprout) [self.spritesBatchNode removeChild: deadSprout.sprite cleanup: YES]; [self.sprouts removeObject: deadSprout];
Ora esegui il gioco e vedrai i germogli che svaniscono quando colpiti e alla fine scompaiono.
Ora dobbiamo aggiungere il rilevamento delle collisioni tra il missile e il bruco. Questa interazione è ciò che rende davvero la tua app un gioco. Una volta colpito, il bruco dovrebbe dividersi nel segmento colpito e ogni nuovo "bruco" dovrebbe viaggiare in direzioni separate. Il segmento colpito è diventato un germoglio.
Inizia aprendo Missile.m, importando Caterpillar.h e Segment.h e aggiungi il seguente codice alla fine del file aggiornare:
metodo:
__block Caterpillar * hitCaterpillar = nil; __block Segment * hitSegment = nil; // 1 [self.gameLayer.caterpillars enumerateObjectsUsingBlock: ^ (id obj, NSUInteger idx, BOOL * stop) Caterpillar * caterpillar = (Caterpillar *) obj; [caterpillar.segments enumerateObjectsUsingBlock: ^ (id obj, NSUInteger idx, BOOL * stop) Segment * segment = (Segment *) obj; CGRect segmentRect = [segmento getBounds]; // 2 if (CGRectIntersectsRect (missileRect, segmentRect)) self.dirty = YES; hitCaterpillar = [caterpillar retain]; hitSegment = [segment retain]; * stop = SÌ; ]; ]; // 3 if (hitCaterpillar && hitSegment) [self.gameLayer splitCaterpillar: hitCaterpillar atSegment: hitSegment]; [versione di hitSegment]; [rilascio di hitCaterpillar];
Ora che abbiamo il missile in collisione con il bruco, ci sono alcune cose che dobbiamo fare. Il primo è scrivere la logica per dividere il bruco su un dato segmento. Questo sarà fatto in GameLayer.m. Innanzitutto, apri GameLayer.h e aggiungi le seguenti dichiarazioni di classe forward.
@class Caterpillar; @class Segment;
Quindi, dichiara il seguente metodo:
- (vuoto) splitCaterpillar: (Caterpillar *) caterpillar atSegment: segmento (segmento);
Prima di iniziare con l'implementazione, è necessario aggiungere due file al progetto. Scarica NSArray + Reverse, decomprimilo e trascina entrambi i file nel tuo progetto. È semplicemente una categoria su NSMutableArray che ci offre un metodo inverso. Ora, apri GameLayer.m, importa Segment.h e NSArray + Reverse.h e implementa il seguente metodo:
- (vuoto) splitCaterpillar: (Caterpillar *) caterpillar atSegment: segmento (Segmento *) // 1 if ([conteggio caterpillar.segmenti] == 1) [self.spritesBatchNode removeChild: segment.sprite cleanup: NO]; [self.caterpillars removeObject: caterpillar]; [self createSproutAtPostion: segment.position]; ritorno; // 2 [self.spritesBatchNode removeChild: segment.sprite cleanup: NO]; // 3 [self createSproutAtPostion: segment.position]; // 4 NSInteger indexOfSegement = [caterpillar.segments indexOfObject: segment]; NSMutableArray * headSegments = [NSMutableArray array]; NSMutableArray * tailsSegments = [NSMutableArray array]; // 5 [caterpillar.segments enumerateObjectsUsingBlock: ^ (id obj, NSUInteger idx, BOOL * stop) if (idx < indexOfSegement) [headSegments addObject:obj]; else if(idx > indexOfSegement) [tailsSegments addObject: obj]; ]; // 6 if ([tailsSegments count]> 0) // 7 [tailsSegments reverse]; // 8 Caterpillar * newCaterpillar = [[[allocazione Caterpillar] initWithGameLayer: segmenti self: tailsSegmenti livello: self.level] autorelease]; newCaterpillar.position = [[tailsSegments objectAtIndex: 0] position]; // 9 if (caterpillar.currentState == CSRight || caterpillar.previousState == CSRight) // Era diretto a destra se (caterpillar.currentState == CSDownLeft || caterpillar.currentState == CSDownRight) // Sta scendendo newCaterpillar .previousState = CSUpRight; else // Sta andando su newCaterpillar.previousState = CSDownRight; newCaterpillar.currentState = CSLeft; else // Si stava dirigendo verso sinistra se (caterpillar.currentState == CSDownLeft || caterpillar.currentState == CSDownRight) // sta scendendo newCaterpillar.previousState = CSUpRight; else // Sta andando su newCaterpillar.previousState = CSDownRight; newCaterpillar.currentState = CSRight; [self.caterpillars addObject: newCaterpillar]; // 10 if ([headSegments count]> 0) caterpillar.segments = headSegments; else [self.caterpillars removeObject: caterpillar];
Wow, c'era molto da accettare. Sei ancora con me? Ci sono un paio di metodi che abbiamo usato qui e nel codice precedente che deve ancora essere implementato. Il primo metodo per implementare è createSproutAtPosition:
. Aggiungi la seguente dichiarazione di metodo all'interfaccia privata nella parte superiore di GameLayer.m:
- (Vuoto) createSproutAtPostion: posizione (CGPoint);
Ora, implementa il seguente metodo:
- (void) createSproutAtPostion: (CGPoint) position // 1 int x = (position.x - kGameAreaStartX) / kGridCellSize; int y = (kGameAreaStartY - kGridCellSize + kGameAreaHeight + kGridCellSize / 2 - position.y) / kGridCellSize; // 2 Sprout * sprout = [[Sprout alloc] initWithGameLayer: self]; sprout.position = position; [self.sprouts addObject: sprout]; _locations [x] [y] = YES;
L'ultimo metodo che dobbiamo implementare per far funzionare tutto questo è initWithGameLayer: segmenti: Livello:
nella classe del bruco. Questo metodo sarà responsabile della creazione di un nuovo bruco dai segmenti che sono stati passati. Apri Caterpillar.h e aggiungi la seguente dichiarazione di metodo:
- (id) initWithGameLayer: (GameLayer *) segmenti di livello: (NSMutableArray *) livello dei segmenti: livello (NSInteger);
Ora apri Caterpillar.m e implementa il seguente metodo:
- (id) initWithGameLayer: (GameLayer *) segmenti di livello: (NSMutableArray *) livello di segmenti: (NSInteger) level if (self = [super initWithGameLayer: layer]) self.segments = segments; self.level = level; self.currentState = CSRight; self.previousState = CSDownLeft; // imposta la posizione del resto dei segmenti __block int x = 0; __block Segment * parentSegment = [self.segments objectAtIndex: 0]; parentSegment.parent = nil; [self.segments enumerateObjectsUsingBlock: ^ (id obj, NSUInteger idx, BOOL * stop) Segment * segment = (Segment *) obj; if (x ++> 0) if (! [segment isEqual: parentSegment]) segment.parent = parentSegment; parentSegment = segment; ]; return self;
Questo metodo è quasi identico al nostro precedente initWithGameLayer: Livello: Posizione:
metodo. L'unica differenza è piuttosto che allocare un array di segmenti, imposta l'array dei segmenti sui segmenti in ingresso che vengono passati.
Vai avanti e avvia il gioco in questa fase. Dovresti essere in grado di uccidere completamente il bruco in gioco.
L'ultima cosa di cui abbiamo bisogno per completare il cerchio della vita di collisione è implementare le collisioni tra il bruco e il giocatore. Se qualche parte del bruco colpisce il giocatore, vogliamo ridurre il numero di vite del giocatore. In aggiunta a ciò, il giocatore diventerà invincibile per un breve momento, in modo che il bruco non divampi proprio attraverso di lui.
Inizia aprendo GameConfig.h e aggiungendo la seguente opzione:
#define kPlayerInvincibleTime 15
Ora apri Caterpillar.m, importa Player.h e aggiungi il seguente codice in fondo al file aggiornare:
metodo appena prima di impostare la posizione del bruco:
static int playerInvincibleCount = kPlayerInvincibleTime; player BOOL staticoHit; CGRect playerRect = [self.gameLayer.player getBounds]; // 1 [self.segments enumerateObjectsUsingBlock: ^ (id obj, NSUInteger idx, BOOL * stop) Segment * segment = (Segment *) obj; CGRect segmentRect = [segmento getBounds]; if (CGRectIntersectsRect (segmentRect, playerRect) && playerInvincibleCount == kPlayerInvincibleTime) * stop = YES; playerHit = YES; // 2 self.gameLayer.player.lives--; [[NSNotificationCenter defaultCenter] postNotificationName: kNotificationPlayerLives object: nil]; ]; // 3 if (playerHit) if (playerInvincibleCount> 0) playerInvincibleCount--; else playerHit = NO; playerInvincibleCount = kPlayerInvincibleTime;
Ora avvia il gioco e lascia che il bruco ti colpisca. Dovrebbe rimuovere una delle tue vite dalla parte superiore dello schermo.
Il rilevamento delle collisioni non è mai un compito facile e abbiamo solo graffiato la superficie. Se vuoi approfondire il rilevamento delle collisioni, dai un'occhiata a Box2D con Cocos2D.
Nel prossimo e ultimo tutorial della serie, discuteremo del gioco polacco. Questo includerà le condizioni vincenti, punteggio, suoni, game over e punteggio alto.
Felice codifica!