Это пятая часть нашей серии руководств по Cocos2D по клонированию сороконожки для iOS. Убедитесь, что вы завершили предыдущие части, прежде чем начать.
Последний раз…
В последнем уроке мы обсуждали искусственный интеллект гусеницы. Вы узнали, как перемещать гусеницу по полю прорастания, а также по границам уровня.
В сегодняшнем уроке мы будем прыгать повсюду, обсуждая движение / взаимодействие игрока и запуск ракет.
Шаг 1: Управление игроком
Управление игроком на самом деле довольно просто. Первое, что нам нужно сделать, это попросить наш игровой слой проглотить прикосновения, чтобы взаимодействовать с ним. Добавьте следующий код в метод 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;
}
}
}
|
- Сначала нам нужно реализовать метод
ccTouchesBegan
чтобы сообщить вызывающей стороне, что мы реагируем на прикосновения. Если вы пропустите это, ваша игра потерпит крах. - Далее, это метод
ccTouchesMoved
которыйccTouchesMoved
когда игрок перетаскивает палец на устройство. - Получить ссылку на координаты текущего местоположения касания и координаты предыдущего местоположения касания.
- Получите изменение от старого местоположения до нового. Мы будем использовать это изменение, чтобы определить, как далеко игрок должен двигаться.
- Здесь у нас есть ряд проверок, чтобы убедиться, что игрок остается в «зоне игрока» или в виртуальной ограничительной рамке, которую мы для него установили. В противном случае мы не выполняем код в # 7, который фактически перемещает игрока.
- Обновите позицию игрока.
- Здесь мы проверяем, сталкивается ли игрок с любым из ростков. Ростки представляют собой препятствия для игрока, и мы хотим ограничить движение игрока, если они мешают ему.
- Наконец, если есть столкновение, верните позицию игрока в его последнюю хорошую позицию.
Теперь, если вы запустите игру, вы сможете перетаскивать ее в любое место на игровой площадке и перемещать игрока в пределах ограниченного пространства.
Шаг 2: Ракетный объект
Ракетный объект — это то, что позволяет игроку и другим игровым объектам взаимодействовать. Игрок будет стрелять постоянным потоком ракет, скорость и частота которого будут варьироваться в зависимости от текущего уровня.
Прежде чем мы начнем создавать Ракету, нам нужно установить несколько констант, которые будут использоваться. Откройте 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
|
- Наш метод init выглядит очень знакомым и отвечает только за создание спрайта Ракеты.
- Вот как быстро будет двигаться ракета. Он начинается с базового значения и ускоряется в зависимости от текущего уровня.
- В какой-то момент скорость нашей ракеты может выйти из-под контроля. Мы предотвращаем это, добавляя максимальную скорость.
- Эти две линии — то, что фактически двигает ракету вперед. Мы рассчитываем новый y для ракеты и обновляем позицию.
- Наконец, если ракета сталкивается с вершиной, установите для ее свойства
_dirty
значение true. Мы будем собирать грязные ракеты внутри GameLayer.
Шаг 3: стрельба ракетами
Теперь, когда у нас есть ракеты, мы должны отстрелить их от игрока. Как правило, когда у вас есть большое количество объектов, таких как ракеты, вы хотите повторно использовать как можно больше, а не размещать новые объекты во время работы основного цикла. Чтобы решить эту проблему, мы создадим два массива. Один массив будет содержать все ракеты, которые в данный момент находятся в игре (то есть запущены игроком), а другой будет содержать пул ракет, которые еще не выпущены. В конце метода обновления мы очистим все 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];
}
|
- Инициализировать каждый из ракетных массивов
-
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];
}
}
|
- Нам нужно создать статический счетчик, чтобы помочь увеличить частоту, с которой запускаются ракеты.
- Рассчитывает частоту стрельбы ракетами. Как уровень увеличивается, так и частота.
- Мы хотим выпустить новую ракету, только если текущая частота больше или равна указанной частоте для уровня
- Извлекает ракету из массива ожидания (если там есть) и добавляет ее в массив стрельбы. Также на этом этапе мы добавляем спрайт ракеты в узел партии для рисования.
- Перечисляет все ракеты, проверяющие их на грязные. Если мы найдем один, вспомним, какой это, чтобы мы могли переместить его обратно в массив ожидания.
- Если была грязная ракета, переместите ее из очереди на массив стрельбы и удалите ее спрайт из узла дозирования.
Это весь код, необходимый для анимации ракет. Идите вперед и запустите игру на этом этапе и наблюдайте, как ракеты стреляют от игрока, когда вы двигаетесь вокруг.
Вывод
Теперь, когда мы включили анимацию некоторых игроков и ракет, это действительно начинает ощущаться как игра. Мы также сделали базовое обнаружение столкновений между объектом игрока и объектом прорастания. В следующем уроке из этой серии мы углубимся в обнаружение столкновений, когда установим столкновения между ракетами и ростками, ракетами и многоножкой, игроком и многоножкой.
Удачного кодирования!