Более сложные монстры и больше уровней: Как сделать простую игру для iPhone на Cocos2d

Ray Wenderlich

Пост также есть для: Упрощенный китайский, Английский, Испанский

Берегись зеленого!

Берегись зеленого!

На данный момент, игра, которую мы делаем, Как сделать простую игру для iPhone на Cocos2D выглядит вполне ничего. У нас есть вращающаяся башня, монстры, по которым мы стреляем и звуковые эффекты.

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

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

Более сложные монстры

Давайте, для развлечения, создадим два типа монстров: один будет слабый, но быстрый, а второй сильный, но медленный. Чтобы игроку было удобнее их различать – скачайте изображение нового монстра и добавьте его в проект. Также скачайте звук взрыва и тоже добавьте его в проект.

Теперь давайте создадим класс Монстр. Есть много способов создать такой класс, но мы пойдём самым простым путём и унаследуем наш класс монстра от класса CCSprite. А от класса монстра мы унаследуем ещё два класса: один для слабого и быстрого, второй для сильного и медленного.

Нажмите FileNew File, выберите Cocoa Touch ClassObjective-C class, убедитесь что это класс наследуется от NSObject и кликните Next. назовите этот файл Monster.m и убедитесь что выбрана опция “Also create Monster.h”.

Затем замените содержимое файла Monster.h следующим кодом:

#import "cocos2d.h"
 
@interface Monster : CCSprite {
    int _curHp;
    int _minMoveDuration;
    int _maxMoveDuration;
}
 
@property (nonatomic, assign) int hp;
@property (nonatomic, assign) int minMoveDuration;
@property (nonatomic, assign) int maxMoveDuration;
 
@end
 
@interface WeakAndFastMonster : Monster {
}
+(id)monster;
@end
 
@interface StrongAndSlowMonster : Monster {
}
+(id)monster;
@end

Код достаточно понятный: мы наследуем монстра от CCSprite, добавляем ему несколько переменных, чтобы отслеживать его состояние, и затем наследуем от него ещё два класса для двух разных типов монстров.

Теперь откройте Monster.m и добавьте реализацию класса:

#import "Monster.h"
 
@implementation Monster
 
@synthesize hp = _curHp;
@synthesize minMoveDuration = _minMoveDuration;
@synthesize maxMoveDuration = _maxMoveDuration;
 
@end
 
@implementation WeakAndFastMonster
 
+ (id)monster {
 
    WeakAndFastMonster *monster = nil;
    if ((monster = [[[super alloc] initWithFile:@"Target.png"] autorelease])) {
        monster.hp = 1;
        monster.minMoveDuration = 3;
        monster.maxMoveDuration = 5;
    }
    return monster;
 
}
 
@end
 
@implementation StrongAndSlowMonster
 
+ (id)monster {
 
    StrongAndSlowMonster *monster = nil;
    if ((monster = [[[super alloc] initWithFile:@"Target2.png"] autorelease])) {
        monster.hp = 3;
        monster.minMoveDuration = 6;
        monster.maxMoveDuration = 12;
    }
    return monster;
 
}
 
@end

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

Теперь давайте интегрируем нашего нового монстра в остальной код. Сперва добавьте импорт нового файла в верхней части HelloWorldScene.m:

#import "Monster.h"

Теперь давайте модифицируем метод addTarget таким образом, чтобы создавать объекты нашего нового класса вместо создания непосредственно спрайтов. Вместо строчки spriteWithFile напишите следующее:

//CCSprite *target = [CCSprite spriteWithFile:@"Target.png" rect:CGRectMake(0, 0, 27, 40)]; 
Monster *target = nil;
if ((arc4random() % 2) == 0) {
    target = [WeakAndFastMonster monster];
} else {
    target = [StrongAndSlowMonster monster];
}

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

int minDuration = target.minMoveDuration; //2.0;
int maxDuration = target.maxMoveDuration; //4.0;

И наконец, пара изменений в методе update. Сначала добавим переменную булева типа перед объявлением targetsToDelete:

BOOL monsterHit = FALSE;

А затем, внутри CGRectIntersectsRec, вместо немедленного добавления объекта в targetsToDelete, напишите такой код:

//[targetsToDelete addObject:target];	
monsterHit = TRUE;
Monster *monster = (Monster *)target;
monster.hp--;
if (monster.hp

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

Теперь измените projectilesToDelete следующим образом:

if (monsterHit) {
    [projectilesToDelete addObject:projectile];
    [[SimpleAudioEngine sharedEngine] playEffect:@"explosion.caf"];
}

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

Screenshot of different monster types

Больше уровней

Чтобы в игре было много уровней, необходимо выполнить некоторый рефакторинг. Рефакторинг довольно простой, но его много, и если его включить в данный туториал, то это будет долго и скучно.

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

Абстрагирование класса Уровень. На данный момент HelloWorldScene хардкодит информацию об “уровне”, например о том, каких монстров порождать, как часто, и так далее. Так что наш первый шаг – это перенести эту информацию в класс Уровень, так чтобы мы могли неоднократно использовать одинаковую логику в HelloWorldScene на разных уровнях.

Повторное использование сцен. На данный момент мы создаём новый экземпляр класса сцены каждый раз при переключении между сценами. Одним из недостатков того подхода является то, что вы можете столкнуться с задержками, поскольку ресурсы загружаются в методе init.

Поскольку у нас простая игра, то мы будем просто создавать один экземпляр каждой сцены и вызывать метод reset() для того, чтобы убрать её предыдущее состояние (например монстры и пули с последнего уровня).

Использование делегата приложения (app delegate) для управления переключениями. На данный момент мы не храним на глобальном уровне никаких переменных для текущего уровня, например, номер уровня уровня или какие-нибудь настройки для него, вместо этого в каждой сцене вручную прописана информация о том, куда переключиться дальше.

Поскольку все сцены имеют доступ к App Delegate, мы добавим туда переменные которые будут хранить в себе информацию об уровнях. Также мы добавим в App Delegate методы для переключения между сценами, чтобы централизовать эту логику и снизить зависимости между сценами.

Это основные моменты рефакторинга – ознакомьтесь с образцом проекта чтобы увидеть все подробности. Помните, что это лишь один из способов реализации такой логики – если у вас есть другой классный способ организации сцен и игровых объектов – пожалуйста напишите о нём!

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

И это всё!

Ещё раз, если до сих пор не скачали, вот образец проекта со всем кодом, разработанным на данный момент.

Теперь, когда вы знаете, как сделать простую игру – почему бы не сделать шаг вперёд и не ознакомиться со следующим туториалом – как сделать игру на основе тайлов в Cocos2D. В конце-концов, кому не нравятся ниндзя, поедающие арбузы?

Я надеюсь вам понравилась эта серия учебников и желаю удачи в разработке игр на Cocos2D!

Ray Wenderlich

Ray is an indie software developer currently focusing on iPhone and iPad development, and the administrator of this site. He’s the founder of a small iPhone development studio called Razeware, and is passionate both about making apps and teaching others the techniques to make them.

When Ray’s not programming, he’s probably playing video games, role playing games, or board games.

Комментарии Пользователя

1 Comment

  • In Russian :
    ???????? ??????? ?? ????! ???????? ?? ????? ????????. ??????? ??? ??? ?????????? ? ?? ??? ??????? ??????? .
    In English by Google :
    Many thanks for the lesson! More such examples. The main thing that they are working and they really learn.
    mugikvi

Other Items of Interest

Ежемесячная рассылка Рэя

Sign up to receive a monthly newsletter with my favorite dev links, and receive a free epic-length tutorial as a bonus!

Разместите у Нас Рекламу!

Hang Out With Us!

Every month, we have a free live Tech Talk - come hang out with us!


Coming up in May: Procedural Level Generation in Games with Kim Pedersen.

Sign Up - May

Coming up in June: WWDC Keynote - Podcasters React! with the podcasting team.

Sign Up - June

Vote For Our Next Book!

Help us choose the topic for our next book we write! (Choose up to three topics.)

    Загрузка ... Загрузка ...

Наши книги

Наша Команда

Туториал Команда

  • Andy Pereira

... 55 общее количество!

Команда Редакторов

  • Alexis Gallagher

... 22 общее количество!

Code Team

  • Orta Therox

... 1 общее количество!

Команда переводчиков

  • Team Tyran
  • Marina Mukhina
  • Jose De La Roca

... 38 общее количество!

Эксперты

  • Richard Casey

... 4 общее количество!