Статьи

Создайте игру Caterpillar с Cocos2D: заключительные шаги

Это седьмая и последняя часть нашей серии руководств по Cocos2D по клонированию Сороконожка для iOS. Убедитесь, что вы завершили предыдущие части, прежде чем начать.

В последнем уроке мы обсуждали, как сделать простое обнаружение столкновений между всеми объектами в нашей игре.

В сегодняшнем уроке мы подведем итоги, обсудив выигрыш, условия победы, игровой звук и игру поверх экрана.


На данный момент, когда вы уничтожаете гусеницу, ничего не происходит. Способ продвинуться вперед состоит в том, чтобы повысить уровень и перезапустить с новыми побегами и новой гусеницей. Это легко сделать, поскольку мы строили игру, чтобы поддерживать это с самого начала. Откройте GameLayer.m и добавьте следующий метод:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
— (void)checkNextLevel {
    if([self.caterpillars count] == 0) { //1
 
        self.level++;
 
        // 3
        CGPoint startingPosition = ccp(kGameAreaStartX, kGameAreaHeight + kGameAreaStartY — kGridCellSize / 2);
        Caterpillar *caterpillar = [[[Caterpillar alloc] initWithGameLayer:self level:self.level position:startingPosition] autorelease];
        [self.caterpillars addObject:caterpillar];
 
        // 4
        int minSproutCount = kStartingSproutsCount + self.level * 2;
        if([self.sprouts count] < minSproutCount) {
            int numberOfSproutsToPlace = minSproutCount — [self.sprouts count];
            for(int x = 0; x < numberOfSproutsToPlace; x++) {
                [self placeRandomSprout];
            }
        }
 
    }
}
  1. Проверьте, не осталось ли гусениц
  2. Увеличить уровень
  3. Создайте новую гусеницу и добавьте ее в игру (на основе нового уровня)
  4. Добавьте ростки в соответствии с minSproutCount

Теперь, когда у нас реализован метод, мы должны вызывать его каждый раз, когда гусеница попадает в цель. Внутри splitCaterpillar:atSegment: метод добавьте следующую строку перед оператором return внутри первого оператора if:

1
2
3
4
5
6
if([caterpillar.segments count] == 1) {
    // …
    // Add this line
    [self checkNextLevel];
    return;
}

Это состояние, когда гусеница представляет собой всего один сегмент. В дополнение к добавлению здесь, добавьте его в самый конец этого метода. Это должно охватывать все случаи.

Если вы запустите игру на этом этапе, вы сможете играть бесконечно, скорость гусеницы увеличивается на каждом уровне.


В игре есть несколько мест, где игрок может увеличить свой счет. Они есть:

  • Бить росток
  • Удар гусеницы
  • Переход на следующий уровень

Мы собираемся немного попрыгать, чтобы добавить подсчет очков к каждому из этих действий, так что оставайтесь со мной.

Прежде чем мы начнем обновлять счет, нам нужно определить еще три константы, которые будут базовыми для оценки. Откройте GameConfig.h и добавьте следующие 3 строки:

1
2
3
#define kSproutHitPoints 25
#define kCaterpillarHitPoints 200
#define kNextLevelPoints 1000

Вы увидите, как они используются, когда мы немного углубимся в это руководство.

Давайте начнем с добавления к счету игроков, когда они попали в росток в игре. Откройте Missile.m, импортируйте Player.h и добавьте следующий код в цикл, который проверяет столкновение с ростком:

1
2
3
4
self.gameLayer.player.score += kSproutHitPoints +
            (arc4random() % self.gameLayer.level) *
            (arc4random() % self.gameLayer.level);
[[NSNotificationCenter defaultCenter] postNotificationName:kNotificationPlayerScore object:nil];

Это увеличивает счет игрока на основе базовых очков и некоторой степени случайности на основе текущего уровня. Таким образом, когда игрок поднимается по уровню, он получает больше очков за уничтожение побегов.

Как и при настройке начального счета, нам нужно опубликовать уведомление, которое обновит метку счета игрока. Это делается при каждом обновлении счета. Если вы запустите игру в этот момент, вы должны видеть, что метка счета игрока обновляется каждый раз, когда вы попадаете в росток.

Следующее место, которое мы собираемся добавить, — это когда гусеница поражена. В самом низу метода update: в Missile.m добавьте следующий код перед разбиением гусеницы:

01
02
03
04
05
06
07
08
09
10
if(hitCaterpillar && hitSegment) {
 
    // Add these lines
    self.gameLayer.player.score += kCaterpillarHitPoints +
    (arc4random() % self.gameLayer.level) *
    (arc4random() % self.gameLayer.level);
    [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationPlayerScore object:nil];
 
    // … code to split the caterpillar …
}

Этот код не сильно отличается от того, что вы видели выше. Последнее место для добавления очков — это когда игрок поднимается по уровню. Этот код будет размещен внутри метода checkNextLevel который вы написали выше. Откройте GameLayer.m, перейдите к методу checkNextLevel и добавьте следующий код сразу после оператора if:

1
2
self.player.score += kNextLevelPoints * self.level;
[[NSNotificationCenter defaultCenter] postNotificationName:kNotificationPlayerScore object:nil];

Здесь нет сюрпризов. Если вы хотите добавить некоторые вариации к оценке в вашей собственной игре, не стесняйтесь изменять значения счета.


На данный момент ваш игрок имеет Game Genie и имеет бесконечные жизни. Нам нужно это изменить. Сначала создайте новый файл с именем GameOverLayer.m, который расширяет CCLayer.

Добавьте следующий код в GameOverLayer.h:

1
2
3
4
5
6
7
8
9
#import «cocos2d.h»
#import «GameConfig.h»
 
@interface GameOverLayer : CCLayer
@property (nonatomic, assign) NSInteger score;
@property(nonatomic, retain) CCLabelTTF *scoreLabel;
@property(nonatomic, retain) CCLabelTTF *highScoreLabel;
+(CCScene *) sceneWithScore:(NSInteger) score;
@end

Я объясню, что каждое из этих свойств и методов делает во время реализации. Теперь добавьте следующий код в GameOverLayer.m

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#import «GameOverLayer.h»
#import «GameLayer.h»
#import «SimpleAudioEngine.h»
 
@implementation GameOverLayer
 
@synthesize score = _score;
@synthesize scoreLabel = _scoreLabel;
@synthesize highScoreLabel = _highScoreLabel;
 
// 1
+(CCScene *) sceneWithScore:(NSInteger) score
{
    // ‘scene’ is an autorelease object.
    CCScene *scene = [CCScene node];
 
    // ‘layer’ is an autorelease object.
    GameOverLayer *layer = [GameOverLayer node];
    layer.score = score;
 
    // add layer as a child to scene
    [scene addChild: layer];
 
    // return the scene
    return scene;
}
 
// 2
— (void)dealloc {
    [_scoreLabel release];
    [_highScoreLabel release];
    [super dealloc];
}
 
-(id) init
{
    if( (self=[super init])) {
        // 3
        [CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGB565];
        CCSprite * background = [CCSprite spriteWithFile:@»game-over.png»];
        background.anchorPoint = ccp(0,0);
        [self addChild:background];
 
        // 4
        _scoreLabel = [[CCLabelTTF labelWithString:@»0″
                                          dimensions:CGSizeMake(320, 30)
                                           alignment:UITextAlignmentCenter
                                            fontName:@»Helvetica»
                                            fontSize:30] retain];
        _scoreLabel.anchorPoint = ccp(0,0);
 
        _scoreLabel.position = ccp(0,155);
        [self addChild:_scoreLabel];
 
        _highScoreLabel = [[CCLabelTTF labelWithString:[NSString stringWithFormat:@»High: %d»,0]
                                        dimensions:CGSizeMake(320, 35)
                                         alignment:UITextAlignmentCenter
                                          fontName:@»Helvetica»
                                          fontSize:30] retain];
        _highScoreLabel.anchorPoint = ccp(0,0);
        _highScoreLabel.color = (ccColor3B){255,0,0};
 
        _highScoreLabel.position = ccp(0,195);
        [self addChild:_highScoreLabel];
 
        // 5
        [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
        [[SimpleAudioEngine sharedEngine] playEffect:@»game-over.caf»];
 
    }
    return self;
}
 
— (void)setScore:(NSInteger)score {
    _score = score;
    self.scoreLabel.string = [NSString stringWithFormat:@»Score: %d»,_score];
 
    // 6
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSInteger highScore = [defaults integerForKey:@»CentipedeHighScore»];
 
    // 7
    if(score > highScore) {
        highScore = score;
        [defaults setInteger:score forKey:@»CentipedeHighScore»];
        [defaults synchronize];
    }
 
    self.highScoreLabel.string = [NSString stringWithFormat:@»High: %d»,highScore];
}
 
// 8
— (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
    [[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:.5 scene:[GameLayer scene] withColor:ccWHITE]];
    return YES;
}
 
 
@end
  1. Это наш стандартный метод для создания новой сцены. Единственная разница в том, что он берет NSInteger и устанавливает его в текущий счет.
  2. Убирать
  3. Визуализировать фон
  4. Установите метки для отображения счета и рекорда
  5. Включить касания
  6. Посмотрите на высокий балл
  7. Сравните новый счет с самым высоким, сохраните новый, если он выше.
  8. Начните новую игру, если игрок касается экрана

Вот скриншот того, как должен выглядеть этот экран:

Game Over

Последний шаг — открыть GameLayer.m и добавить следующие строки в конец метода updateLives :

1
2
3
if (lifeCount == 0) {
    [[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:.5 scene:[GameOverLayer sceneWithScore:self.player.score] withColor:ccWHITE]];
}

Это проверяет, установлены ли жизни на 0. Если это так, мы заменяем текущую сцену сценой Game Over.


Последний шаг в нашей игре — добавить звук. Сначала загрузите звуки ниже и добавьте их в свой проект:

GameSounds.zip

Традиционно обработка звука была довольно болезненной в игре OpenGL. Вы должны будете использовать что-то вроде OpenAL или другую сложную библиотеку C ++. Cocos2D значительно упростил работу с библиотекой SimpleAudioEngine . Это дает вам возможность легко воспроизводить фоновую музыку, а также быстрые звуки.

Опять же, мы будем прыгать совсем немного. Так что, если размещение какого-либо кода неясно для вас, пожалуйста, спросите меня в комментариях или обратитесь к исходному коду этого руководства.

Откройте AppDelegate.m и импортируйте SimpleAudioEngine.h и добавьте следующую строку в конец метода applicationDidFinishLaunching .

1
[[SimpleAudioEngine sharedEngine] playBackgroundMusic:@»background.caf»];i

Это оно! Для воспроизведения фоновой музыки на протяжении всего времени воспроизведения необходима только одна строка. Теперь нам просто нужно воспроизводить звуковые эффекты в ответ на различные действия.

В Caterpillar.m, когда Caterpillar сталкивается с игроком (ближе к концу update: метод):

1
[[SimpleAudioEngine sharedEngine] playEffect:@»player-hit.caf»];

В Missile.m, когда Ракета попадает в Росток:

1
[[SimpleAudioEngine sharedEngine] playEffect:@»sprout-hit.caf»];

Также в Missile.m, когда Ракета попадает в Гусеницу:

1
[[SimpleAudioEngine sharedEngine] playEffect:@»caterpillar-hit.caf»];

В методе init в GameOverLayer.m:

1
[[SimpleAudioEngine sharedEngine] playEffect:@»game-over.caf»];

Это должно охватывать все звуки, которые я использовал в игре. Кроме того, обязательно импортируйте SimpleAudioEngine.h в каждый из перечисленных выше классов.


На этом мы завершаем серию из 7 частей по созданию игры Caterpillar с использованием Cocos2D для iPhone. К настоящему времени у вас должно быть четкое понимание того, как проектировать и создавать простую игру, используя игровой движок Cocos2D. Если у вас есть какие-либо вопросы или комментарии, пожалуйста, оставляйте их в разделе комментариев здесь или пишите мне в Twitter .

Удачного кодирования!