Статьи

Создайте игру Caterpillar с Cocos2D: движение и ракеты

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


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

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


Управление игроком на самом деле довольно просто. Первое, что нам нужно сделать, это попросить наш игровой слой проглотить прикосновения, чтобы взаимодействовать с ним. Добавьте следующий код в метод init в GameLayer.m:

1
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];

Как вы видели в первом уроке, эта строка кода просто позволит GameLayer быть делегатом касания, позволяющим ему получать уведомления о касаниях. Теперь есть два метода, которые нам нужно реализовать. Добавьте следующие методы в GameLayer.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
// 1
— (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
    return YES;
}
 
// 2
— (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event {
    // 3
    CGPoint touchLocation = [self convertTouchToNodeSpace:touch];
 
    CGPoint oldTouchLocation = [touch previousLocationInView:touch.view];
    oldTouchLocation = [[CCDirector sharedDirector] convertToGL:oldTouchLocation];
    oldTouchLocation = [self convertToNodeSpace:oldTouchLocation];
 
    // 4
    int xChange = touchLocation.x — oldTouchLocation.x;
    int yChange = touchLocation.y — oldTouchLocation.y;
 
    int newX = self.player.position.x + xChange;
    int newY = self.player.position.y + yChange;
 
    // 5
    if(newX < kGameAreaStartX + kGameAreaWidth — kGridCellSize &&
       newX >
       newY >
       newY <
 
 
        __block BOOL collide = NO;
        CGPoint oldPosition = self.player.position;
        // 6
        self.player.position = ccp(newX,newY);
 
        // 7
        [self.sprouts enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            Sprout *sprout = (Sprout *)obj;
            CGRect sproutRect = [sprout getBounds];
            CGRect playerRect = [self.player getBounds];
 
            if(CGRectIntersectsRect(sproutRect, playerRect)) {
                collide = YES;
                *stop = YES;
            }
 
        }];
 
        // 8
        if(collide) {
            self.player.position = oldPosition;
        }
    }
}
  1. Сначала нам нужно реализовать метод ccTouchesBegan чтобы сообщить вызывающей стороне, что мы реагируем на прикосновения. Если вы пропустите это, ваша игра потерпит крах.
  2. Далее, это метод ccTouchesMoved который ccTouchesMoved когда игрок перетаскивает палец на устройство.
  3. Получить ссылку на координаты текущего местоположения касания и координаты предыдущего местоположения касания.
  4. Получите изменение от старого местоположения до нового. Мы будем использовать это изменение, чтобы определить, как далеко игрок должен двигаться.
  5. Здесь у нас есть ряд проверок, чтобы убедиться, что игрок остается в «зоне игрока» или в виртуальной ограничительной рамке, которую мы для него установили. В противном случае мы не выполняем код в # 7, который фактически перемещает игрока.
  6. Обновите позицию игрока.
  7. Здесь мы проверяем, сталкивается ли игрок с любым из ростков. Ростки представляют собой препятствия для игрока, и мы хотим ограничить движение игрока, если они мешают ему.
  8. Наконец, если есть столкновение, верните позицию игрока в его последнюю хорошую позицию.

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


Ракетный объект — это то, что позволяет игроку и другим игровым объектам взаимодействовать. Игрок будет стрелять постоянным потоком ракет, скорость и частота которого будут варьироваться в зависимости от текущего уровня.

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

1
2
3
4
5
#define kMissileSpeed 1.0
#define kMissileMaxSpeed 10.0
#define kMissilesTotal 20
#define kMissileFrequency .6 //seconds
#define kMinMissileFrequency .2

Я объясню каждый из них, как они появляются в следующем коде. Теперь создайте новый подкласс GameObject под названием Missile. Добавьте следующий код в Missile.h:

1
2
3
4
5
6
7
#import «cocos2d.h»
#import «GameObject.h»
 
@interface Missile : GameObject
@property (nonatomic, assign) BOOL dirty;
— (void)update:(ccTime)dt;
@end

Свойство dirty будет использоваться в будущем для обозначения того, что эта ракета больше не находится в игре. Это может быть из-за того, что он исчезает с экрана или сталкивается с другим игровым объектом. Поскольку ракеты постоянно движутся, им потребуется метод обновления для их анимации.

Теперь откройте Missile.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
#import «Missile.h»
#import «GameLayer.h»
#import «GameConfig.h»
 
@implementation Missile
 
@synthesize dirty = _dirty;
 
// 1
— (id)initWithGameLayer:(GameLayer *)layer {
 
    if(self == [super initWithGameLayer:layer]) {
        self.sprite = [CCSprite spriteWithSpriteFrameName:@»missile.png»];
    }
 
    return self;
}
 
— (void)update:(ccTime)dt {
    // 2
    int inc = kMissileSpeed * (self.gameLayer.level + 1.5);
 
    // 3
    if(inc > kMissileMaxSpeed) {
        inc = kMissileMaxSpeed;
    }
 
    // 4
    int y = self.position.y + inc;
    self.position = ccp(self.position.x,y);
 
    // 5
    if(self.position.y > kGameAreaStartY + kGameAreaHeight) {
        self.dirty = YES;
    }
}
 
@end
  1. Наш метод init выглядит очень знакомым и отвечает только за создание спрайта Ракеты.
  2. Вот как быстро будет двигаться ракета. Он начинается с базового значения и ускоряется в зависимости от текущего уровня.
  3. В какой-то момент скорость нашей ракеты может выйти из-под контроля. Мы предотвращаем это, добавляя максимальную скорость.
  4. Эти две линии — то, что фактически двигает ракету вперед. Мы рассчитываем новый y для ракеты и обновляем позицию.
  5. Наконец, если ракета сталкивается с вершиной, установите для ее свойства _dirty значение true. Мы будем собирать грязные ракеты внутри GameLayer.

Теперь, когда у нас есть ракеты, мы должны отстрелить их от игрока. Как правило, когда у вас есть большое количество объектов, таких как ракеты, вы хотите повторно использовать как можно больше, а не размещать новые объекты во время работы основного цикла. Чтобы решить эту проблему, мы создадим два массива. Один массив будет содержать все ракеты, которые в данный момент находятся в игре (то есть запущены игроком), а другой будет содержать пул ракет, которые еще не выпущены. В конце метода обновления мы очистим все dirty ракеты и переместим их из массива «в игре» в массив «пул».

Добавьте следующие свойства в ваш файл GameLayer.h:

1
2
@property (nonatomic, retain) NSMutableArray *missilesWaiting;
@property (nonatomic, retain) NSMutableArray *missilesFiring;

Теперь откройте GameLayer.m, импортируйте Missile.h и добавьте следующие строки в ваш метод init:

1
2
3
4
5
6
7
8
9
// 1
_missilesWaiting = [[NSMutableArray alloc] initWithCapacity:kMissilesTotal];
_missilesFiring = [[NSMutableArray alloc] initWithCapacity:kMissilesTotal];
// 2
for(int x = 0; x < kMissilesTotal; x++) {
    Missile *missile = [[Missile alloc] initWithGameLayer:self];
    [self.missilesWaiting addObject:missile];
    [missile release];
}
  1. Инициализировать каждый из ракетных массивов
  2. kMissilesTotal время kMissilesTotal и создайте столько объектов Missile. Как только они созданы, мы добавляем их в массив missilesWaiting .

Затем перейдите к методу обновления и добавьте следующий код:

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
// 1
static float missleFireCount = 0;
 
— (void)update:(ccTime)dt {
 
    // …Caterpillar code…
 
    // 2
    float frequency = kMinMissileFrequency;
    if(kMissileFrequency / (self.level * 1.25) > kMinMissileFrequency) {
        frequency = kMissileFrequency / self.level;
    }
 
    // 3
    if(missleFireCount < frequency) {
        missleFireCount += dt;
    } else {
        missleFireCount = 0;
        // 4
        if([self.missilesWaiting count] > 0) {
            Missile *missile = [self.missilesWaiting objectAtIndex:0];
            [self.missilesFiring addObject:missile];
            [self.missilesWaiting removeObjectAtIndex:0];
            missile.position = self.player.position;
            [self.spritesBatchNode addChild:missile.sprite];
 
        }
    }
 
    // 5
    __block Missile *dirty = nil;
    [self.missilesFiring enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        Missile *missile = (Missile *)obj;
        [missile update:dt];
        if(missile.dirty) {
            dirty = missile;
            *stop = YES;
        }
    }];
 
    // 6
    if(dirty) {
        dirty.dirty = NO;
        [self.missilesWaiting addObject:dirty];
        [self.missilesFiring removeObject:dirty];
        [self.spritesBatchNode removeChild:dirty.sprite cleanup:NO];
    }
}
  1. Нам нужно создать статический счетчик, чтобы помочь увеличить частоту, с которой запускаются ракеты.
  2. Рассчитывает частоту стрельбы ракетами. Как уровень увеличивается, так и частота.
  3. Мы хотим выпустить новую ракету, только если текущая частота больше или равна указанной частоте для уровня
  4. Извлекает ракету из массива ожидания (если там есть) и добавляет ее в массив стрельбы. Также на этом этапе мы добавляем спрайт ракеты в узел партии для рисования.
  5. Перечисляет все ракеты, проверяющие их на грязные. Если мы найдем один, вспомним, какой это, чтобы мы могли переместить его обратно в массив ожидания.
  6. Если была грязная ракета, переместите ее из очереди на массив стрельбы и удалите ее спрайт из узла дозирования.

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

Missiles


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

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