C’est le sixième volet de notre série de didacticiels Cocos2D sur le clonage de Centipede pour iOS. Assurez-vous d'avoir complété les parties précédentes avant de commencer.
Dans le dernier tutoriel, je vous ai montré comment créer un tableau d'objets missiles et en tirer un flux constant. Vous avez également appris l’interaction de base entre les joueurs dans Cocos2D afin de déplacer le lecteur..
Dans le tutoriel d'aujourd'hui, nous allons explorer comment configurer de base détection de collision dans Cocos2D. Bien que cette solution ne soit pas toujours optimale, elle est certainement la plus rapide et la plus facile à mettre en œuvre..
La collision de missiles avec les germes ressemble beaucoup à la collision entre le joueur et les germes. Nous vérifions simplement les limites de chaque missile en jeu par rapport aux limites de chaque pousse en jeu et déterminons s’il ya collision. Quand une pousse a été atteinte, on décrémente sa vie, on modifie son opacité et on l'enlève si sa vie atteint 0.
Ouvrez Missile.m, importez Sprout.h et ajoutez le code suivant au bas de la méthode de mise à jour:
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--; ];
Au fur et à mesure que chaque missile se met à jour, il énumère tous les germes en jeu, en vérifiant si leurs droits de croisement se croisent. En cas de collision, nous réglons le missile sur "sale" et décrémentons la vie de la pousse en conséquence. Nous avons déjà écrit le code pour modifier l'opacité des germes en fonction de leur durée de vie. Ainsi, si vous lancez le jeu à ce stade, vous devriez voir les germes changer lorsqu'ils sont touchés. Le problème est qu'ils sont toujours en jeu. Nous devons éliminer les germes avec 0 vies.
Cela peut être fait à l'intérieur du mettre à jour:
méthode dans GameLayer.m. Ouvrez GameLayer.m et ajoutez le code suivant au bas de la mettre à jour:
méthode:
// 1 __block Sprout * deadSprout = nil; [self.sprouts enumerateObjectsUsingBlock: ^ (id obj, NSUInteger idx, BOOL * arrêt) Sprout * sprout = (Sprout *) obj; if (sprout.lives == 0) deadSprout = sprout; * stop = YES; ]; // 2 if (deadSprout) [self.spritesBatchNode removeChild: deadSprout.sprite cleanup: YES]; [self.sprouts removeObject: deadSprout];
Maintenant, lancez le jeu et vous verrez les pousses se faner une fois touchées et éventuellement disparaître.
Nous devons maintenant ajouter la détection de collision entre le missile et la chenille. Cette interaction est ce qui fait de votre application un jeu. Une fois touchée, la chenille doit se fendre au segment touché et chaque nouvelle "chenille" doit voyager dans des directions distinctes. Le segment qui a été touché est ensuite transformé en une pousse.
Commencez par ouvrir Missile.m, importez Caterpillar.h et Segment.h, puis ajoutez le code suivant au bas de la fenêtre. mettre à jour:
méthode:
__block Caterpillar * hitCaterpillar = nil; __block Segment * hitSegment = nil; // 1 [self.gameLayer.caterpillars enumerateObjectsUsingBsingBlock: ^ (id obj, NSUInteger idx, BOOL * stop) Caterpillar * chenille = (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 = [chenille conservée]; hitSegment = [segment retenue]; * stop = YES; ]; ]; // 3 if (hitCaterpillar && hitSegment) [self.gameLayer splitCaterpillar: hitCaterpillar atSegment: hitSegment]; [hitSegment release]; [hitCaterpillar release];
Maintenant que le missile entre en collision avec la chenille, nous devons faire certaines choses. La première consiste à écrire la logique permettant de scinder la chenille sur un segment donné. Cela sera fait dans GameLayer.m. Tout d’abord, ouvrez GameLayer.h et ajoutez les déclarations de classe suivantes:.
@class Caterpillar; @class Segment;
Ensuite, déclarez la méthode suivante:
- (vide) splitCaterpillar: (Caterpillar *) chenille atSegment: (segment *) segment;
Avant de commencer l'implémentation, nous devons ajouter deux fichiers au projet. Téléchargez NSArray + Reverse, décompressez-le et faites glisser les deux fichiers dans votre projet. C'est simplement une catégorie sur NSMutableArray qui nous donne une méthode inverse. Maintenant, ouvrez GameLayer.m, importez Segment.h et NSArray + Reverse.h et implémentez la méthode suivante:
- (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]; revenir; // 2 [self.spritesBatchNode removeChild: segment.sprite cleanup: NO]; // 3 [self createSproutAtPostion: segment.position]; // 4 NSInteger indexOfSegement = [caterpillar.segments indexOfObject: segment]; NSMutableArray * headSegments = [tableau NSMutableArray]; NSMutableArray * tailsSegments = [tableau NSMutableArray]; // 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 = [[[Allocation Caterpillar] initWithGameLayer: segments auto: tailsSegments niveau: self.level] autorelease]; newCaterpillar.position = [[tailsSegments objectAtIndex: 0] position]; // 9 if (caterpillar.currentState == CSRight || caterpillar.previousState == CSRight) // se dirigeait bien si (caterpillar.currentState == CSDownLeft || caterpillar.currentState == CSDownRight) // se dirige vers newCaterpillar .previousState = CSUpRight; else // se dirige vers newCaterpillar.previousState = CSDownRight; newCaterpillar.currentState = CSLeft; else // était à gauche si (caterpillar.currentState == CSDownLeft || caterpillar.currentState == CSDownRight) // est en train de descendre newCaterpillar.previousState = CSUpRight; else // se dirige vers 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'était beaucoup à prendre. Tu es toujours avec moi? Quelques méthodes que nous avons utilisées ici et dans le code précédent doivent encore être implémentées. La première méthode à implémenter est createSproutAtPosition:
. Ajoutez la déclaration de méthode suivante à votre interface privée en haut de GameLayer.m:
- (void) createSproutAtPostion: (CGPoint) position;
Maintenant, implémentez la méthode suivante:
- (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] = OUI;
La dernière méthode que nous devons mettre en œuvre pour que tout cela fonctionne est initWithGameLayer: segments: niveau:
dans la classe chenille. Cette méthode sera chargée de construire une nouvelle chenille à partir des segments transférés. Ouvrez Caterpillar.h et ajoutez la déclaration de méthode suivante:
- (id) initWithGameLayer: segments de couche (GameLayer *): niveau de segments (NSMutableArray *): niveau (NSInteger);
Maintenant, ouvrez Caterpillar.m et implémentez la méthode suivante:
- (id) initWithGameLayer: (GameLayer *) segments de couche: (NSMutableArray *) niveau de segments: (NSInteger) niveau if (self = [super initWithGameLayer: layer]) self.segments = segments; self.level = level; self.currentState = CSRight; self.previousState = CSDownLeft; // définit la position du reste des segments __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; ]; retourner soi-même;
Cette méthode est presque identique à notre précédente initWithGameLayer: niveau: position:
méthode. La seule différence est que, plutôt que d'allouer un tableau de segments, il définit le tableau de segments sur les segments entrants transmis..
Allez-y et lancez le jeu à ce stade. Vous devriez être capable de tuer complètement la chenille en jeu..
La dernière chose dont nous avons besoin pour compléter le cercle de détection de collision de la vie est de mettre en œuvre les collisions entre la chenille et le joueur. Si une partie de la chenille frappe le joueur, nous voulons réduire le nombre de vies du joueur. En plus de cela, le joueur deviendra invincible pendant un bref moment, de sorte que la chenille ne flambe pas juste à travers lui.
Commencez par ouvrir GameConfig.h et en ajoutant l'option suivante:
#define kPlayerInvincibleTime 15
Maintenant, ouvrez Caterpillar.m, importez Player.h et ajoutez le code suivant au bas de la mettre à jour:
méthode juste avant de définir la position de la chenille:
static int playerInvincibleCount = kPlayerInvincibleTime; statique BOOL playerHit; 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, objet: nil]; ]; // 3 if (playerHit) if (playerInvincibleCount> 0) playerInvincibleCount--; else playerHit = NO; playerInvincibleCount = kPlayerInvincibleTime;
Maintenant, lancez le jeu et laissez la chenille vous frapper. Il devrait supprimer une de vos vies du haut de l'écran.
La détection de collision n'est jamais une tâche facile et nous n'avons fait qu'effleurer la surface. Si vous souhaitez approfondir la détection des collisions, jetez un coup d'œil à l'utilisation de Box2D avec Cocos2D..
Dans le prochain et dernier tutoriel de la série, nous discuterons du peaufinement du jeu. Cela inclura les conditions gagnantes, le score, les sons, le jeu fini et le meilleur score.
Code heureux!