Dit is het zesde deel van onze Cocos2D-lessenreeks over het klonen van Centipede voor iOS. Zorg ervoor dat je de voorgaande delen hebt voltooid voordat je begint.
In de laatste zelfstudie heb ik je laten zien hoe je een reeks raket-objecten maakt en een constante stroom van ze afvuurt. Je hebt ook geleerd over elementaire spelerinteractie in Cocos2D om de speler te verplaatsen.
In de tutorial van vandaag zullen we onderzoeken hoe we kunnen instellen basis- botsingsdetectie in Cocos2D. Hoewel dit niet altijd de optimale oplossing is, is het zeker de snelste en gemakkelijkst te implementeren.
De raketbotsing met de spruiten lijkt veel op de botsing van de speler met de spruiten. We controleren eenvoudig de grenzen van elke raket in het spel tegen de grenzen van elke sprout in het spel en bepalen of er een botsen. Wanneer een spruit is geraakt, verlagen we de levensduur, passen we de dekking aan en verwijderen deze als de levensduur 0 is.
Open Missile.m, importeer Sprout.h en voeg de volgende code toe aan de onderkant van de updatemethode:
CGRect-raketRect = [zelf getBounds]; [self.gameLayer.sprouts enumerateObjectsUsingBlock: ^ (id obj, NSUInteger idx, BOOL * stop) Sprout * sprout = (Sprout *) obj; CGRect spruitRect = [spruit getBounds]; if (CGRectIntersectsRect (missileRect, sproutRect)) self.dirty = YES; sprout.lives--; ];
Terwijl elke raket wordt bijgewerkt, somt het alle spruiten op die in het spel zijn en wordt gecontroleerd of hun begrenzingsrechters elkaar kruisen. Als er een botsing is, zetten we de raket op "vuil" en verlagen de levens van de spruit dienovereenkomstig. We hebben de code al geschreven om de dekking van de spruiten aan te passen op basis van de levens, dus als je het spel nu uitvoert, zou je de spruiten moeten zien veranderen als ze geraakt worden. Het probleem is dat ze nog steeds spelen. We moeten kiemen verwijderen met 0 levens.
Dit kan gedaan worden in de bijwerken:
methode in GameLayer.m. Open GameLayer.m en voeg de volgende code toe aan de onderkant van de bijwerken:
methode:
// 1 __block Sprout * deadSprout = nihil; [self.sprouts enumerateObjectsUsingBlock: ^ (id obj, NSUInteger idx, BOOL * stop) Sprout * sprout = (Sprout *) obj; if (sprout.lives == 0) deadSprout = ontspruiten; * stop = JA; ]; // 2 if (deadSprout) [self.spritesBatchNode removeChild: deadSprout.sprite opruimen: YES]; [self.sprouts removeObject: deadSprout];
Voer het spel nu uit en je ziet de spruitjes vervagen wanneer ze worden geraakt en uiteindelijk verdwijnen.
We moeten nu botsingsdetectie toevoegen tussen de raket en de rups. Deze interactie maakt jouw app echt tot een spel. Eenmaal geraakt, zou de rups zich moeten splitsen in het hit-segment en elke nieuwe "rups" zou in verschillende richtingen moeten reizen. Het segment dat werd geraakt wordt vervolgens omgezet in een spruit.
Begin met het openen van Missile.m, importeer Caterpillar.h en Segment.h en voeg de volgende code toe aan de onderkant van de bijwerken:
methode:
__blok Caterpillar * hitCaterpillar = nil; __blok Segment * hitSegment = nihil; // 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 = [segment getBounds]; // 2 if (CGRectIntersectsRect (missileRect, segmentRect)) self.dirty = YES; hitCaterpillar = [rupsband behouden]; hitSegment = [segment behouden]; * stop = JA; ]; ]; // 3 if (hitCaterpillar && hitSegment) [self.gameLayer splitCaterpillar: hitCaterpillar atSegment: hitSegment]; [hitSegment-release]; [hitCaterpillar-release];
Nu dat de raket tegen de rups botst, zijn er een paar dingen die we moeten doen. De eerste is om de logica te schrijven om de rups op een bepaald segment te splitsen. Dit gebeurt in GameLayer.m. Open eerst GameLayer.h en voeg de volgende forward class-declaraties toe.
@class Caterpillar; @class-segment;
Verklaar vervolgens de volgende methode:
- (void) splitCaterpillar: (Caterpillar *) caterpillar atSegment: (Segment *) segment;
Voordat we met de implementatie beginnen, moeten we twee bestanden aan het project toevoegen. Download NSArray + Reverse, pak het uit en sleep beide bestanden naar uw project. Het is gewoon een categorie op NSMutableArray die ons een omgekeerde methode geeft. Open GameLayer.m nu, importeer Segment.h en NSArray + Reverse.h en voer de volgende methode uit:
- (void) splitCaterpillar: (Caterpillar *) caterpillar atSegment: (Segment *) segment // 1 if ([caterpillar.segments count] == 1) [self.spritesBatchNode removeChild: segment.sprite cleanup: NO]; [self.caterpillars removeObject: caterpillar]; [self createSproutAtPostion: segment.position]; terug te keren; // 2 [self.spritesBatchNode removeChild: segment.sprite opruimen: NEE]; // 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 = [[[Caterpillar alloc] initWithGameLayer: self segments: tailsSegments level: self.level] autorelease]; newCaterpillar.position = [[tailsSegments objectAtIndex: 0] positie]; // 9 if (caterpillar.currentState == CSRight || caterpillar.previousState == CSRight) // Werd naar rechts geleid als (caterpillar.currentState == CSDownLeft || caterpillar.currentState == CSDownRight) // gaat naar beneden newCaterpillar .previousState = CSUpRight; else // Staat op nieuwCaterpillar.previousState = CSDownRight; newCaterpillar.currentState = CSLeft; else // was op weg naar links als (caterpillar.currentState == CSDownLeft || caterpillar.currentState == CSDownRight) // gaat naar beneden newCaterpillar.previousState = CSUpRight; else // Staat op nieuwCaterpillar.previousState = CSDownRight; newCaterpillar.currentState = CSRight; [self.caterpillars addObject: newCaterpillar]; // 10 if ([headSegments count]> 0) caterpillar.segments = headSegments; else [self.caterpillars removeObject: caterpillar];
Wauw, dat was veel om op te nemen. Ben je nog steeds bij me? Er zijn een aantal methoden die we hier en in de vorige code hebben gebruikt die nog moeten worden geïmplementeerd. De eerste methode om te implementeren is createSproutAtPosition:
. Voeg de volgende methodeverklaring toe aan uw persoonlijke interface bovenaan GameLayer.m:
- (Void) createSproutAtPostion: (CGPoint) positie;
Implementeer nu de volgende methode:
- (void) createSproutAtPostion: (CGPoint) positie // 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 = positie; [self.sprouts addObject: sprout]; _locaties [x] [y] = JA;
De laatste methode die we moeten implementeren om dit allemaal te laten werken is initWithGameLayer: segmenten: level:
in de caterpillar-klasse. Deze methode zal verantwoordelijk zijn voor het bouwen van een nieuwe caterpillar uit de segmenten die zijn doorgegeven. Open Caterpillar.h en voeg de volgende methodeverklaring toe:
- (id) initWithGameLayer: (GameLayer *) -segmenten: (NSMutableArray *) segmentsniveau: (NSInteger) -niveau;
Open nu Caterpillar.m en voer de volgende methode uit:
- (id) initWithGameLayer: (GameLayer *) -segmenten: (NSMutableArray *) segmentsniveau: (NSInteger) niveau if (self = [super initWithGameLayer: layer]) self.segments = segments; self.level = niveau; self.currentState = CSRight; self.previousState = CSDownLeft; // stel de positie in van de rest van de segmenten __block int x = 0; __block Segment * parentSegment = [self.segments objectAtIndex: 0]; parentSegment.parent = nihil; [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; ]; terugkeer zelf;
Deze methode is bijna identiek aan onze vorige initWithGameLayer: level: positie:
methode. Het enige verschil is dat in plaats van het toewijzen van een array van segmenten, het de segmentarray instelt op de inkomende segmenten die worden doorgegeven.
Ga je gang en voer het spel in dit stadium uit. Je zou de rups die in het spel is volledig moeten kunnen doden.
Het laatste dat we moeten doen om de botsingsdetectiekring van het leven te voltooien, is het implementeren van de botsingen tussen de rups en de speler. Als een deel van de rups de speler raakt, willen we het aantal levens van de speler verminderen. Bovendien wordt de speler voor een kort moment onoverwinnelijk, zodat de rups niet alleen door hem heen schiet.
Begin door GameConfig.h te openen en de volgende optie toe te voegen:
#define kPlayerInvincibleTime 15
Open nu Caterpillar.m, importeer Player.h en voeg de volgende code toe aan de onderkant van de bijwerken:
methode net voordat u de positie van de rups instelt:
static int playerInvincibleCount = kPlayerInvincibleTime; statische BOOL spelerHit; CGRect playerRect = [self.gameLayer.player getBounds]; // 1 [self.segments enumerateObjectsUsingBlock: ^ (id obj, NSUInteger idx, BOOL * stop) Segment * segment = (segment *) obj; CGRect segmentRect = [segment getBounds]; if (CGRectIntersectsRect (segmentRect, playerRect) && playerInvincibleCount == kPlayerInvincibleTime) * stop = YES; playerHit = YES; // 2 self.gameLayer.player.lives--; [[NSNotificationCenter defaultCenter] postNotificationName: kNotificationPlayerLives-object: nul]; ]; // 3 if (playerHit) if (playerInvincibleCount> 0) playerInvincibleCount--; else playerHit = NO; playerInvincibleCount = kPlayerInvincibleTime;
Voer het spel nu uit en laat de rups je raken. Het zou een van je levens moeten verwijderen vanaf de bovenkant van het scherm.
Botsingsdetectie is nooit een gemakkelijke taak en we hebben alleen het oppervlak bekrast. Als je dieper in botsingsdetectie wilt graven, bekijk dan Box2D met Cocos2D.
In de volgende en laatste zelfstudie van de serie bespreken we de game polish. Dit omvat de winnende voorwaarden, score, geluiden, game-over en hoge score.
Happy Coding!