Dette er den sjette delen av vår Cocos2D opplæringsserie om kloning av Centipede for iOS. Pass på at du har fullført de forrige delene før du begynner.
I den siste opplæringen viste jeg deg hvordan du lager en rekke rakettobjekter og brenner en konstant strøm av dem. Du lærte også om grunnleggende spillerinteraksjon i Cocos2D for å flytte spilleren.
I dagens veiledning vil vi undersøke hvordan du skal sette opp grunnleggende kollisjonsdeteksjon i Cocos2D. Selv om dette ikke alltid er den optimale løsningen, er det definitivt den raskeste og enkleste å implementere.
Missil kollisjonen med spirer er mye som spilleren kollisjon med spirer. Vi kontrollerer bare grensene til hvert missil i lek mot grensene til hver spire i spill og avgjør om noen kolliderer. Når en spire er truffet, reduserer vi livet, endrer opaciteten, og fjerner det hvis livet når 0.
Åpne opp Missile.m, importer Sprout.h, og legg til følgende kode nederst i oppdateringsmetoden:
CGRect missileRect = [self getBounds]; [self.gameLayer.prouts enumerateObjectsUsingBlock: ^ (id obj, NSUInteger idx, BOOL * stopp) Sprout * sprout = (Sprout *) obj; CGRect sproutRect = [sprout getBounds]; hvis (CGRectIntersectsRect (missileRect, sproutRect)) self.dirty = YES; sprout.lives--; ];
Som hver missil oppdateringer, det oppregner alle spirer i spill, kontroller for å se om deres avgrensende rettene krysser. Hvis det er en kollisjon, setter vi raketten til "skitten" og reduserer spruitens liv tilsvarende. Vi har allerede skrevet koden for å modifisere spiralens opasitet basert på dets liv, så hvis du kjører spillet på dette punktet, bør du se spirene endres når de blir rammet. Problemet er at de fortsatt er i spill. Vi må fjerne spirer med 0 liv.
Dette kan gjøres inne i Oppdater:
metode i GameLayer.m. Åpne GameLayer.m og legg til følgende kode i bunnen av Oppdater:
metode:
// 1 __block Sprout * deadSprout = null; [self.sprouts enumerateObjectsUsingBlock: ^ (id obj, NSUInteger idx, BOOL * stopp) Sprout * sprout = (Sprout *) obj; hvis (sprout.lives == 0) deadSprout = sprout; * Stopp = JA; ]; // 2 hvis (deadSprout) [self.spritesBatchNode removeChild: deadSprout.sprite cleanup: JA]; [self.sprouts removeObject: deadSprout];
Nå løp spillet og du vil se spirene fading når du treffer og til slutt forsvinner.
Vi må nå legge til kollisjonsdeteksjon mellom missilet og larven. Denne interaksjonen er det som virkelig gjør appen din et spill. Når en gang er slått, bør larven splitte seg på slagssegmentet og hver ny "caterpillar" skal reise i separate retninger. Segmentet som ble rammet, blir så omgjort til en spire.
Start med å åpne Missile.m, importere Caterpillar.h og Segment.h, og legg til følgende kode nederst på siden Oppdater:
metode:
__block Caterpillar * hitCaterpillar = nil; __block Segment * hitSegment = nil; // 1 [self.gameLayer.caterpillars enumerateObjectsUsingBlock: ^ (id obj, NSUInteger idx, BOOL * stopp) Caterpillar * caterpillar = (Caterpillar *) obj; [caterpillar.segments enumerateObjectsUsingBlock: ^ (id obj, NSUInteger idx, BOOL * stopp) Segment * segment = (Segment *) obj; CGRect segmentRect = [segment getBounds]; // 2 hvis (CGRectIntersectsRect (missileRect, segmentRect)) self.dirty = YES; hitCaterpillar = [caterpillar behold]; hitSegment = [segment behold]; * Stopp = JA; ]; ]; // 3 hvis (hitCaterpillar && hitSegment) [self.gameLayer splitCaterpillar: hitCaterpillar atSegment: hitSegment]; [hitSegment release]; [hitCaterpillar release];
Nå som vi har missilen kolliderer med larven, er det noen ting vi må gjøre. Den første er å skrive logikken for å dele larven på et gitt segment. Dette vil bli gjort i GameLayer.m. Først åpner du GameLayer.h og legger til følgende fremoverklassdeklarasjoner.
@ klasse Caterpillar; @ klasse Segment;
Deretter erklæres følgende metode:
- (void) splitCaterpillar: (Caterpillar *) caterpillar atSegment: (Segment *) segmentet;
Før vi begynner med implementeringen, må vi legge til to filer til prosjektet. Last ned NSArray + Omvendt, pakke ut det, og dra begge filene inn i prosjektet. Det er rett og slett en kategori på NSMutableArray som gir oss en omvendt metode. Nå, åpne GameLayer.m, importere Segment.h og NSArray + Reverse.h, og implementer følgende metode:
- (tomt) splitCaterpillar: (Caterpillar *) caterpillar atSegment: (Segment *) segment // 1 hvis ([caterpillar.segments count] == 1) [selv.spritesBatchNode removeChild: segment.sprite cleanup: NO]; [self.caterpillars removeObject: caterpillar]; [self createSproutAtPostion: segment.position]; komme tilbake; // 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 * stopp) if (idx < indexOfSegement) [headSegments addObject:obj]; else if(idx > indexOfSegement) [tailsSegments addObject: obj]; ]; // 6 hvis ([tailsSegments count]> 0) // 7 [halerSegmenter omvendt]; // 8 Caterpillar * newCaterpillar = [[[Caterpillar alloc] initWithGameLayer: selvsegmenter: halerSegmentnivå: self.level] autorelease]; newCaterpillar.position = [[tailsSegments objectAtIndex: 0] posisjon]; // 9 hvis (caterpillar.currentState == CSRight || caterpillar.previousState == CSRight) // Gikk rett hvis (caterpillar.currentState == CSDownLeft || caterpillar.currentState == CSDownRight) // Skal ned newCaterpillar .previousState = CSUpRight; ellers // Er på vei opp newCaterpillar.previousState = CSDownRight; newCaterpillar.currentState = CSLeft; ellers // Var overskriften til venstre hvis (caterpillar.currentState == CSDownLeft || caterpillar.currentState == CSDownRight) // Skal ned newCaterpillar.previousState = CSUpRight; ellers // Er på vei opp newCaterpillar.previousState = CSDownRight; newCaterpillar.currentState = CSRight; [self.caterpillars addObject: newCaterpillar]; // 10 hvis ([headSegments count]> 0) caterpillar.segments = headSegments; ellers [self.caterpillars removeObject: caterpillar];
Wow, det var mye å ta med. Er du fortsatt med meg? Det er et par metoder vi brukte her og i forrige kode som fortsatt må implementeres. Den første metoden for å implementere er createSproutAtPosition:
. Legg til følgende metodedeklarasjon til ditt private grensesnitt øverst på GameLayer.m:
- (Void) createSproutAtPostion: (CGPoint) posisjon;
Nå, implementer følgende metode:
- (void) createSproutAtPostion: (CGPoint) posisjon // 1 int x = (position.x - kGameAreaStartX) / kGridCellSize; int y = (kGameAreaStartY - kGridCellSize + kGameAreaHeight + kGridCellSize / 2 - posisjon.y) / kGridCellSize; // 2 Sprout * sprout = [[Sprout alloc] initWithGameLayer: self]; sprout.position = posisjon; [self.sprouts addObject: spire]; _locations [x] [y] = YES;
Den siste metoden vi må implementere for å gjøre dette alt arbeid er initWithGameLayer: segmenter: Nivå:
i caterpillar-klassen. Denne metoden vil være ansvarlig for å bygge en ny larve fra segmentene som ble sendt inn. Åpne Caterpillar.h og legg til følgende metodedeklarasjon:
- (id) initWithGameLayer: (GameLayer *) lagsegmenter: (NSMutableArray *) segmentnivå: (NSInteger) nivå;
Nå, åpne Caterpillar.m og implementer følgende metode:
- (id) initWithGameLayer: (GameLayer *) lagsegmenter: (NSMutableArray *) segmentnivå: (NSInteger) nivå hvis (selv = [super initWithGameLayer: layer]) self.segments = segmenter; self.level = level; self.currentState = CSRight; self.previousState = CSDownLeft; // sett posisjonen til resten av segmentene __block int x = 0; __block Segment * parentSegment = [self.segments objectAtIndex: 0]; parentSegment.parent = nil; [self.segments enumerateObjectsUsingBlock: ^ (id obj, NSUInteger idx, BOOL * stopp) Segment * segment = (Segment *) obj; if (x ++> 0) if (! [segment erEqual: parentSegment]) segment.parent = parentSegment; parentSegment = segment; ]; returner selv;
Denne metoden er nesten identisk med vår tidligere initWithGameLayer: Nivå: stilling:
metode. Den eneste forskjellen er snarere enn å tilordne en rekke segmenter, den setter segmentgruppene til de innkommende segmentene som sendes inn i.
Gå videre og kjør spillet på dette stadiet. Du bør være i stand til å fullt ut drepe larven som er i spill.
Den endelige tingen vi trenger for å fullføre kollisjonsdeteksjonssirkelen av livet er å implementere kollisjonene mellom larven og spilleren. Hvis noen del av larven treffer spilleren, vil vi redusere spillerens antall liv. I tillegg til at spilleren blir uovervinnelig i et kort øyeblikk, slik at larven ikke bare blar rett gjennom ham.
Start med å åpne GameConfig.h og legg til følgende alternativ:
#define kPlayerInvincibleTime 15
Nå åpner du Caterpillar.m, importerer Player.h og legger til følgende kode nederst på siden Oppdater:
metoden like før du setter larvens posisjon:
statisk int playerInvincibleCount = kPlayerInvincibleTime; statisk BOOL playerHit; CGRect playerRect = [self.gameLayer.player getBounds]; // 1 [self.segments enumerateObjectsUsingBlock: ^ (id obj, NSUInteger idx, BOOL * stopp) Segment * segment = (Segment *) obj; CGRect segmentRect = [segment getBounds]; hvis (CGRectIntersectsRect (segmentRect, playerRect) && playerInvincibleCount == kPlayerInvincibleTime) * stop = YES; playerHit = YES; // 2 self.gameLayer.player.lives--; [[NSNotificationCenter defaultCenter] postNotificationName: kNotificationPlayerLives objekt: null]; ]; // 3 hvis (playerHit) if (playerInvincibleCount> 0) playerInvincibleCount--; ellers playerHit = NO; playerInvincibleCount = kPlayerInvincibleTime;
Nå løp spillet og la larven slå deg. Det bør fjerne et av livene dine fra toppen av skjermen.
Kollisjonsdeteksjon er aldri en enkel oppgave, og vi har bare riper overflaten. Hvis du vil grave dypere inn i kollisjonssensor, ta en titt på å bruke Box2D med Cocos2D.
I neste og siste opplæring av serien, diskuterer vi polishen. Dette vil inkludere vinnende forhold, score, lyder, spill over og høy poengsum.
Glad koding!