Перевод статьи Introduction to CocosBuilder
CocosBuilder – это бесплатный инструмент, который позволяет быстро и легко управлять спрайтами, макетами и сценами при разработке вашей игры, использующей Cocos2D.
CocosBuilder идеально подходит для быстрой планировки меню и других элементов интерфейса игровых сцен, при этом не испытывая проблем по их компоновке в пространстве.
В ходе обучения, вы напишите игру Cat Jump. Вы увидите, как CocosBuilder позволит вам сэкономить массу времени и кода.
Перед тем как появился CocosBuilder, создание даже базового интерфейса в игре на Cocos2D было головной болью для разработчика. Чтобы добавить меню или кнопку в игру, вы обычно делали что-то вроде этого:
- Попытка угадать. “Ммм… думаю, эта кнопка должна быть где-то 50 на 50.”
- Запуск приложения. “Ага, не совсем…”
- Ещё попытка. “Думаю, 60 на 50 будет получше!”
- Проверка и всё по-новой. “Чёрт, всё равно не то. Гррр!”
CocosBuilder устраняет как все массовые итерации по настройке и тестированию позиции спрайта. CocosBuilder освобождает вам время для того, чтобы сосредоточиться на логике игры, а не рвать на себе волосы, продираясь через ваш интерфейс, что делает вас более продуктивным разработчиком — а это всегда хорошо, не так ли?
Это руководство для начинающих покажет вам, как использовать CocosBuilder для создания простых интерфейсов. Вы узнаете, как в CocosBuilder создать меню, кнопки, системы частицы (particle systems), слои, и подключать интерфейсы.
В ходе обучения, вы напишите игру Cat Jump, изначально разработанной для семинара Cocos2D via Minigames. Конечно, в этот раз будет намного меньше кодирования. Вы увидите, как CocosBuilder позволит вам сэкономить массу времени и кода.
Это руководство предполагает, что у вас есть базовые знания Cocos2D. Если вы новичок в Cocos2D, почитайте уроки по Cocos2D сначала.
Игра Cat Jump
Игра Cat Jump — это игра про кота, у которого плохой день. Он просто гуляет по своим делам, идёт по улице, когда внезапно на него натыкаются все подряд — автомобили, грузовики, и даже дети на велосипедах!
Снимок экрана из игры вы видели в начале этого руководства.
Ваш бедный кот имеет ограниченное количество жизней (конечно 9), и ваша цель — остаться в живых как можно дольше, избегая неприятностей.
Для начала, скачайте версию CatJump без CocosBuilder. Откройте его в Xcode, запустите и поиграйте некоторое время — это очень весело!
После того как поиграете, загляните в код — вы увидите тонны, заданных прямо в коде, смещений и позиций меню, текстовых элементов и спрайтов. Это все написать было очень тяжело — вы узнаете о способе сделать это лучше!
Во время обучения вы переделаете игру Cat Jump, выполнив следующие задачи:
- Переделать главное меню с использованием CocosBuilder. Сейчас главного меню сделано путем размещения элементов меню в жестко прописанных в коде координатах — вы удалите этот код и переделаете меню с использованием CocosBuilder.
- Добавить экран Options. Вы добавите новый экран с тремя кнопками выбора уровня сложности: Easy, Medium и Hard. Выбор варианта сложности будет вызывать селектор в коде программы.
- Добавить экран About. Вы также добавите экран с описанием программы и демонстрацией крутого графического эффекта. Используя CocosBuilder, вы не напишите ни строки кода, чтобы сделать это!
- Переделать основной экран игры с использованием CocosBuilder. Вы удалите ручное размещение спрайтов и вместо этого используете CocosBuilder.
- Переделать экран окончания игры с использованием CocosBuilder. Опять же, ни каких жестко прописанных смещений!
Наконец, вы получите полезные советы о том, как искать и устранять распространенные ошибки в CocosBuilder. Вы узнаете об этом сейчас и сэкономите свое время потом.
Приступая к работе с CocosBuilder
Если вы еще этого не сделали, то загрузите CocosBuilder. Убедитесь в том, чтобы скачали последнюю версию, которая на момент написания статьи составляет 2.1. Также скачайте файлы с примерами.
Распакуйте CocosBuilder из скачанного архива и скопируйте его в папку Программы.
Перед тем, как создать новый проект в CocosBuilder, необходимо создать каталог для вашего проекта. Это то место, где вы будете хранить все игровые ресурсы/активы.
Примечание: Вы можете также использовать в своём проекте ссылки на ресурсы, но использовать структуру каталогов, с хранящимися там ресурсами более удобно, так как это позволяет точно мне знать, где все мои файлы и помогает хранить мои ресурсы организовано.
Создайте новую папку на рабочем столе и назовите его CocosBuilderFiles. Затем создайте две подпапки в этой папке и назовите их Resources и Scenes.
Папка Resources будет, очевидно, содержать все ресурсы для игры (спрайты, шрифты и т.д.). Если вы хотите, вы можете скопировать все ресурсы из проекта Cat Jump, который вы скачали ранее, но, чтобы было ещё проще — я создал ZIP файл со всем, что вам понадобиться. Скачайте, распакуйте и скопируйте его содержимое в папку Resources.
Запустите CocosBuilder и выберите пункт меню FileNewNew Project. Назовите проект CatJump и сохраните его в папке CocosBuilderFiles.
Теперь, когда вы создали проект, вы увидите папки ресурсов и сцены на боковой панели проекта в левой части CocosBuilder. Также заметьте, что CocosBuilder автоматически создал новую папку под названием ccbResources. Там же вы увидите новый файл с именем HelloCocosBuilder.ccb. Дважды щелкните по файлу, чтобы увидеть его содержимое.
Это очень простой слой с надписью «Hello CocosBuilder»:
Не волнуйтесь, ваша игра CatJump будет немного посложнее, чем это.
Глянь, мам, вообще нет кода!
Начнём переделку CatJump с создания главного меню игры. Эта сцена будет иметь три кнопки:
- Play – Так запускается игра!
- Options – Покажем сцену с параметрами, где пользователи могут выбрать уровень сложности игры.
- About — Покажем сцене, где будет информация о том, как играть в игру.
И первое, что вам нужно сделать, это удалить файл HelloCocosBuilder.ccb, так как это просто файл-шаблон, созданный CocosBuilder.
Примечание: Можно подумать, что это достаточно просто — удалить неиспользуемый файл сцены из проекта, но я не был в состоянии сделать это прямо из CocosBuilder. Мне пришлось закрыть CocosBuilder, удалить HelloCocosBuilder.ccb из Finder, а затем снова запустить проект в CocosBuilder. Если кто-то знает более простой способ сделать это, пожалуйста, напишите на форуме!
Затем выберите пункт меню FileNewNew File. В появившемся окне убедитесь, что в поле тип корневого объекта (Root object type) выбран CCLayer и поставьте галочки у следующих типов разрешений: iPhone Landscape и iPhone 5 Landscape.
Нажмите кнопку Create, введите имя файла MainMenuScene и сохранить его в папке Scenes.
Панель проекта должен выглядеть примерно так:
Ну, вот, вы создали свою первую сцену! Теперь, как насчёт добавления нескольких спрайтов?
Нажмите на кнопку CCSprite на панели инструментов. Подсказка: Кнопка обведена кругом на изображении ниже.
Так вы добавите новый спрайт на сцену.
Выберите созданный спрайт и установите ему в качестве фрейма файл Title_catjump.png. Для этого используйте правую боковую панель, на которой появляются свойства выбранного элемента. В разделе CCSprite у поля Sprite frame в выпадающем меню выберите файл Title_catjump.png в папке ResourcesNormal.
Затем, поставьте спрайт в центр сцены, просто перетащив его в центр. Или, если вы предпочитаете быть точным, то вы можете установить свойство Anchor Point (в разделе CCNode) в 0 для X и Y значения.
Обратите внимание, что это будет работать только если у вас поле Position установлено в значение нижнего левого угла. Если вы измените значение этого поля, то вы должны соответственно изменить значения X и Y. Развлекитесь — посмотрите, что будет происходить, когда вы поставите различные значения.
Отлично! Теперь у вас есть фоновое изображение. Следующее, что нужно сделать, это добавить кнопки для пунктов меню.
Нажмите пункт CCControlButton панели инструментов, чтобы создать новую кнопку на экране.
Новая кнопка создается с хорошим фоновым изображением, которое вы можете найти в папке ccbResources, созданной CocosBuilder. Дайте этой кнопке название Play, используя раздел CCControlButton на правой боковой панели, поле Title.
Отрегулируйте положение кнопки. Вы можете разместить её где угодно, просто перетаскивая её или установить абсолютную позицию с помощью свойств на правой боковой панели.
Ладно, нам нужно больше кнопок! Повторите описанную выше процедуру, чтобы добавить ещё две. Вторую кнопку назовите Options, третью — About. Ваш окончательный макет должен быть похож на показанный на изображении ниже:
Ура, макет первой сцены завершён!
Подключение слоя к классу
Прежде чем продолжить, необходимо сделать некоторую корректировку. Если вы создаете сцену, используя слой, созданный в CocosBuilder, и если этот слой сцены пользовательского класса, то вы должны сказать CocosBuilder имя этого класса.
Например, если вы инициализируете сцену, используя файл MainMenuScene, и вы хотите чтобы его слой был того класса, который вы создали, то вам нужно указать имя этого класса в разделе Code Connections.
Выберите файлMainMenuScene.ccb и выберите корневой узел CCLayer на шкале времени.
В разделе Code Connections, в поле Custom class напишите название вашего класса MainMenuLayer. Теперь, когда вы инициализируете эту сцену, CocosBuilder будет искать класс с именем MainMenuLayer и использовать его для создания экземпляра слоя сцены.
Далее вам нужно опубликовать файл интерфейса CocosBuilder. Для этого просто выберите пункт меню FilePublish. Так вы создадите новый файл с именем MainMenuScene.ccbi в каталоге Scenes.
Хватит возиться с CocosBuilder – сейчас самое время, чтобы всё это опробовать в Xcode!
Время писать код!
Во-первых, убедитесь, что у вас установлена последняя версия Cocos2D 2.X (2.1-beta4 на момент написания этого учебника).
Затем запустите Xcode и создайте новый проект с шаблоном iOScocos2d v2.xcocos2d. Введите CatJump в поле Product Name, заполните как обычно поле Company Identifier, а в поле Device Family выберите iPhone:
Завершите создание проекта и сохраните его где-нибудь на вашем диске.
Затем, в корне проекта создайте новую группу Scenes, и перетащите туда файл MainMenuScene.ccb, который вы до этого сделали. Убедитесь, что рядом с пунктом «Copy items to destination group’s folder (if needed)» стоит галочка и в секции «Add to targets» около CatJump тоже стоит галочка.
Теперь нужно добавить CCBReader к вашему проекту. CCBReader идёт в комплекте с файлами примеров, которые вы загрузили ранее с сайта CocosBuilder. Разархивируйте файлы примеров (если вы еще это не сделали) в папку на жестком диске. Найдите папку CCBReader в папке Add to Your Projectcocos2d-iphone.
Перетащите всю папку CCBReader в ваш проект. Убедитесь, что выбран пункт «Create groups for any added folders» и стоит галочка у пункта «Copy items into destination group’s folder». Сделайте то же самое с папкой CCControlExtension.
Далее, создайте новую группу в вашем проекте и назовите её Layers. Создайте новый файл с шаблоном iOScocos2d v2.xCCNode. Сделайте его подклассом CCLayer и назовите его MainMenuLayer.m.
Перед тем, как писать код, откройте AppDelegate.m и добавьте следующий оператор импорта (сразу после существующих в файле операторов #import):
#import "CCBReader.h"
Затем найдите процедуру application:didFinishLaunchingWithOptions:, а в ней такую строку:
[director_ pushScene: [IntroLayer scene]];
Как только вы её найдёте, замените её на это:
[director_ pushScene: [CCBReader sceneWithNodeGraphFromFile:@"MainMenuScene.ccbi"]];
И это всё, что нужно сделать в коде, для того, чтобы запустить сцены, созданные с CocosBuilder! Класс CCBReader будет парсить файл MainMenuScene.ccbi и сам создаст сцену!
Но прежде чем вы запустите программу, нужно сделать один последний шаг. Помните то фоновое изображение, что вы добавили в вашу сцену и изображения кнопок из папки ccbResources в папке вашего CocosBuilder-проекта?
Этих изображений нет в проекте, а они нам нужны, чтобы программа нормально работала. В противном случае приложение вызовет ошибку. (На самом деле, вы можете проверить это прямо сейчас, пытавшись запустить программу...)
Возьмите все файлы из папки Resources вашего CocosBuilder-проекта и перетащите их в папку Resources вашего Xcode-проекта. Сделайте то же самое со всем файлам в папке ccbResources. Как и прежде, убедитесь, что стоит галочка около пункта «Copy items into destination group’s folder», что выбран пункт «Create groups for any added folders» и около нашего проекта CatJump стоит галочка.
Теперь запустите программу. Если вы получили сообщение об ошибке во время компиляции CCBReader.m, замените строку с ошибкой на нижеследующую:
return [_bundle pathForResource:resource ofType:ext inDirectory:subpath];
Запустите программу, вы увидите главное меню с тремя кнопками, как показано ниже:
События
Поздравляю, теперь у вас есть работающий макет, созданный в CocosBuilder, только с одной строкой кода! :)
Но как получить событие о том, что пользователь нажал на одну из этих кнопок?
CocosBuilder справиться с этой задачей очень просто! Он позволяет вам указать имя метода, который нужно вызвать, если пользователь нажмёт на кнопку. Вы также можете указать событие, для которого этот метод будет вызван (используя набор параметров).
Давайте добавим эту функциональность в MainMenuScene. Откройте MainMenuScene.ccb в CocosBuilder и выберите кнопку Play. На правой панели, в разделе CCNode измените значение свойства Tag на 1.
Затем, в разделе CCControl заполните текстовое поле Selector именем метода buttonPressed:, который будет вызываться. Также убедитесь, что поле Target содержит значение Document root.
Сделайте то же самое для двух других кнопок, но с разными тегами – у Options – поле Tag будет равно 2, у About — 3.
Удивительно! Вы связали нажатия кнопок с селектором, представленном в CCLayer. Сохраните изменения, опубликуйте MainMenuScene.ccb, и скопируйте опубликованный файл в папку Xcode- проекта.
Примечание: Вам не надо перетаскивать файл в Xcode-проект как раньше, так как файл уже существует в проекте. Так что, либо удалите файл из проекта, а затем перетащите его снова в проект, либо скопируйте с помощью Finder новый файл из папки CocosBuilderFiles в папку с файлами вашего Xcode-проекта.
Теперь откройте MainMenuLayer.m в Xcode и добавьте туда следующие операторы импорта:
#import "CCControlButton.h"
#import "CCBReader.h"
Кроме того, добавить следующее операторы #define с константами сразу ниже операторов #import. Они описывают теги тех кнопок, которые вы поместили на сцене:
#define PLAY_BUTTON_TAG 1
#define OPTIONS_BUTTON_TAG 2
#define ABOUT_BUTTON_TAG 3
Как на счёт метода buttonPressed:? Добавьте его в MainMenuLayer.m:
-(void)buttonPressed:(id)sender {
CCControlButton *button = (CCControlButton*) sender;
switch (button.tag) {
case PLAY_BUTTON_TAG:
[[CCDirector sharedDirector] replaceScene:[CCTransitionCrossFade transitionWithDuration:1.0 scene:[CCBReader sceneWithNodeGraphFromFile:@"GameScene.ccbi"]]];
break;
case OPTIONS_BUTTON_TAG:
[[CCDirector sharedDirector] replaceScene:[CCTransitionFlipAngular transitionWithDuration:1.0 scene:[CCBReader sceneWithNodeGraphFromFile:@"OptionsScene.ccbi"]]];
break;
case ABOUT_BUTTON_TAG:
[[CCDirector sharedDirector] replaceScene:[CCTransitionCrossFade transitionWithDuration:1.0 scene:[CCBReader sceneWithNodeGraphFromFile:@"AboutScene.ccbi"]]];
break;
}
}
В этом методе всё должно быть понятно: тут отдельно обрабатывается нажатие каждой кнопки. Например, когда нажимают кнопку About, показывается сцена AboutScene.ccbi.
Запустите игру. Теперь у вас должно быть полностью функциональное главное меню.
Потрясающе, вы только что создали свою первую сцену, и вы почти не писали никакого кода.
Конечно, вы заметили, что код для buttonPressed: связан с CCBI-файлами, которых вы ещё не делали. Поэтому, если нажать на любую из кнопок в меню, программа выдаст ошибку, так как этих сцены еще нет на месте.
Это то, что вы собираетесь сделать дальше — заполнить пробелы!
Тут нет ничего сложного!
Как и в главном меню, в сцене выбора сложности будет три кнопки, и создание этой сцене будет столь же простым.
В сцене Options, нажатие кнопок позволит пользователю выбрать уровень сложности Easy, Medium или Hard. Там же будет кнопка возвращения в главное меню.
Открытое CocosBuilder и создайте новую сцену, выбрав пункт меню FileNewNew File (выполните те же шаги, что и при создании MainMenuScene), назовите её OptionsScene и сохраните в папке Scenes.
Добавьте на сцену три кнопки и дайте им названия Easy, Medium и Hard. Затем установите их тегами значения 1, 2 и 3, соответственно.
Для получения событий, когда пользователь будет нажимать на кнопки, вы должны указать метод, который будет вызван. Так же, как вы делали с MainMenuScene, напишите для каждой из кнопок в поле Selector значение difficultyButtonPressed: а в поле Target выберите значение Document Root.
Примечание: Интересно, что значит этот Document Root? Это означает корневой узел в дереве временной шкалы «Default Timeline». Скоро вы установите корневому узлу (CCLayer) пользовательский класс — OptionsLayer. Это означает, что в качестве Document Root будет выступать класс OptionsLayer.
Ваш макет будет выглядеть вроде этого:
Теперь кнопка возвращения назад, в главное меню. На этот раз, вместо добавления CCControlButton, нужно добавить пункт меню CCMenu.
Нажмите кнопку CCMenu на панели инструментов.
Так вы создадите меню CCMenu и добавите его в слой OptionsScene. Теперь добавьте пункт меню CCMenuItemImage, нажав на кнопку CCMenuItemImage на панели инструментов.
Укажите в полях Normal и Selected разные спрайты, для каждого свой — btn-back-0.png и btn-back-1.png. Эти свойства могут быть изменены в разделе CCMenuItemImage на панели справа.
Вставьте кнопку «Назад» в верхний левый угол сцены и пропишите для неё селектор backButtonPressed:. Не забудьте выбрать в поле Target значение Document root.
Вот и все! Сцена должна выглядеть следующим образом:
Так же, как раньше вы делали с MainMenuScene, добавьте пользовательский класс для OptionsScene. Назовите его OptionsLayer.
Как и прежде, сохраните изменения, опубликуйте сцену, и добавьте CCBI-файл в Xcode-проект.
Перейдите в Xcode, создайте новый класс в группе Layers и назовите его OptionsLayer (убедитесь, что он подкласс класса CCLayer), всё так же, как вы делали раньше.
Затем добавьте следующие операторы импорта и определения в начало OptionsLayer.m:
#import "CCBReader.h"
#import "CCControlButton.h"
#define DIFFICULTY_EASY_BUTTON_TAG 1
#define DIFFICULTY_MEDIUM_BUTTON_TAG 2
#define DIFFICULTY_HARD_BUTTON_TAG 3
А также следующие методы:
-(void)backButtonPressed:(id)sender {
[[CCDirector sharedDirector] replaceScene:[CCTransitionFlipAngular transitionWithDuration:1.0 scene:[CCBReader sceneWithNodeGraphFromFile:@"MainMenuScene.ccbi"]]];
}
-(void)difficultyButtonPressed:(id)sender {
CCControlButton *button = (CCControlButton*) sender;
NSString *difficultyLevel = @"Hard";
if (button.tag == DIFFICULTY_EASY_BUTTON_TAG) {
difficultyLevel = @"Easy";
} else if(button.tag == DIFFICULTY_MEDIUM_BUTTON_TAG) {
difficultyLevel = @"Medium";
}
NSLog(@"Difficulty is set to %@", difficultyLevel);
}
Все это должно быть вам знакомо. Процедура backButtonPressed: возвращает пользователя назад, в меню главной сцены.
Процедура difficultyButtonPressed: в её нынешнем виде не устанавливает уровень сложности, а только пишет в журнал сам факт выбора пользователя. Не стесняйтесь выбирать высокий уровень сложности, ничего ведь не меняется.
Запустите игру, теперь у вас есть две полностью функциональных сцены. Вы уже на полпути к полному интерфейсу игры!
Пришла очередь… огня!
Сцены About нужны для того, чтобы предоставить пользователям дополнительную информацию о вашей программе — как играть или использовать приложение, кто её сделал, номер версии и так далее.
Ваша сцена About будет особенной: это будет горящий ад! Это не только для того, чтобы выглядеть круто, но и чтобы понять, как добавить систему частиц (например, специальный эффект), используя CocosBuilder.
Переключитесь в CocosBuilder, создайте новый файл с именем AboutScene и сохранить его в каталоге Scenes.
Начните работу над макетом, нажав кнопку CCParticleSystemQuad на панели инструментов.
Так вы создадите огненную систему частиц. Выберите систему частиц и измените свойство Particle texture на значение cat_leap_1.png. Поиграйтесь с параметром CCParticleSystemQuad, пока вы не будете довольны результатом. Затем сдвиньте систему частиц в правый нижний угол экрана, как показано ниже:
Теперь нужно добавить немного текста. Добавьте CCLabelBMFont, нажав на следующую кнопку на панели инструментов:
В свойствах шрифта выберите Arial.fnt (из папки Resources). Затем добавьте еще два текстовых элемента, один под другой, тоже укажите тот же шрифт.
Разбейте текст следующего содержания — Help the cat jump over all the obstacles trying to run him over — между тремя текстовыми элементами. Никаких нюансов здесь больше нет.
Теперь, сцена About будет выглядеть так:
Вы почти закончили с этой сценой! Все, что осталось — кнопка Назад.
Добавьте кнопку Назад в верхний левый угол, как вы это делали раньше. В качестве селектора напишите backButtonPressed:, в поле Target — Document root.
Последний шаг — добавление пользовательского класса для этой сцены, так же как и для двух предыдущих сцен. Имя пользовательского класса — AboutLayer, как показано ниже:
Как и прежде, сохраните изменения, опубликуйте сцену, и добавьте её в Xcode-проект. Затем перейдите в Xcode и создайте новый класс Cocos2D в группе Layers. Назовите его AboutLayer и убедитесь, что он подкласс класса CCLayer.
Откройте AboutLayer.m и добавьте следующий оператор импорта:
#import "CCBReader.h"
А также этот метод:
-(void)backButtonPressed:(id)sender {
[[CCDirector sharedDirector] replaceScene:[CCTransitionFlipAngular transitionWithDuration:1.0 scene:[CCBReader sceneWithNodeGraphFromFile:@"MainMenuScene.ccbi"]]];
}
Метод будет вызван, когда пользователь нажмет кнопку Назад (зелёную стрелку) и метод заменит текущую сцену на сцену MainMenu.
Запустите игру. Проверьте: нажатие на кнопку About отображает сцену AboutScene, а кнопка Назад возвращает вас в главное меню. Не плохо за несколько минут Вашего времени, а?
В игру!
Наконец, настало время, чтобы ввести в игру нашу звезду – кота, у которого будет тяжелый день! Все что вам нужно сделать, так это поставить все спрайты, нужные для игры, в правильные места, и это вы сделаете без ковыряний в коде. Итак, давайте начнем!
Перейдите в CocosBuilder и создайте новый файл. Назовите его GameScene и сохраните в папке Scenes.
Затем нажмите кнопку CCSprite на панели инструментов, чтобы создать новый спрайт. Установите в поле Frame у этого спрайта значение bg.png и поставьте в центра экрана. Ваша сцена должна выглядеть, как показано ниже:
Теперь добавим главного героя игры — кота! Добавьте ещё один спрайт в слой, нажав кнопку CCSprite еще раз. В поле Frame у спрайта выберите cat_stand_1.png и координаты X: 75, Y: 75.
Кроме того, добавим два элемента CCLabelBMFonts. Они будут показывать количество жизней кошки, и количество сделанных прыжков.
Нажмите кнопку CCLabelBMFont два раза и в получившихся элементах введите текст Lives: и Dodges:. У этих элементов становите шрифт Arial.fnt. Поместите получившиеся текстовые элементы в верхний левый и правый углы экрана, как показано ниже:
Отлично! Всё готово.
Или не всё? Спрайты вы разместили, но у вас нет способа сослаться на них в коде. Например, как бы вы будете манипулировать котом в логике игры? Не ломайте голову – с CocosBuilder это просто.
Давайте начнем с нашего героя, кота. Выберите спрайт кота и в правой панели, в разделе Code Connections, вы увидите ниже поля Custom class выпадающее меню.
В этом меню (сейчас там выбрано значение Don’t assign) выберите значение Doc root var и в соседнем текстовом поле напишите имя переменной cat. Так мы связали переменную «cat» с ссылкой на этот спрайт. Значение " Doc root var" говорит о том, что эта переменная будет присутствовать в корневом документе, в нашем случае – классе слоя.
Теперь повторите то же самое для текстовых элементов и свяжите их с переменными livesLabel и dodgesLabel.
Догадываетесь, что будет дальше? Так же, как предыдущие сцены, эта сцена требует связи с классом слоя. Установите связь этой сцены с классом GameLayer, как показано ниже:
Сохраните изменения, опубликуйте сцену, и добавьте CCBI-файл в проект. Перейдите в Xcode и создайте новый класс в группе Layers, назвав его GameLayer и убедившись, что он подкласс CCLayer.
Настало время описать игровую логику. Но так как это руководство по работе с CocosBuilder (а не по построению логики игры), просто замените GameLayer.m этим длинным блоком кода:
#import "GameLayer.h"
#import "CCBReader.h"
#import "SimpleAudioEngine.h"
#define kVehicleTypeNone -1
#define kVehicleTypeRedCar 0
#define kVehicleTypeYellowCar 1
#define kVehicleTypeDog 2
#define kVehicleTypeKid 3
@interface GameLayer() {
CCLabelBMFont *livesLabel;
CCLabelBMFont *dodgesLabel;
CCSprite *cat;
CCNode *_vehicles;
BOOL _invincible;
BOOL _jumping;
double _nextSpawn;
int _lives;
int _dodges;
CCSpriteBatchNode *_catJumpBatchNode;
CCAnimation *_catJumpAnimation;
}
@end
@implementation GameLayer
- (id) init {
self = [super init];
if (self) {
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"CatJumpAtlas.plist"];
_catJumpBatchNode = [CCSpriteBatchNode batchNodeWithFile:@"CatJumpAtlas.png"];
[self addChild:_catJumpBatchNode z:1];
_catJumpAnimation = [CCAnimation animation];
[_catJumpAnimation addSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"cat_leap_1.png"]];
[_catJumpAnimation addSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"cat_leap_2.png"]];
[_catJumpAnimation setDelayPerUnit:0.625f];
[_catJumpAnimation retain];
// If you want to add this to the AnimationCache instead of retaining
//[[CCAnimationCache sharedAnimationCache] addAnimation:catJumpAnimation name:@"catJumpAnim"];
// Dog Animation
CCAnimation *dogAnimation = [CCAnimation animation];
[dogAnimation addSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"dog_1.png"]];
[dogAnimation addSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"dog_2.png"]];
[dogAnimation addSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"dog_3.png"]];
[dogAnimation addSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"dog_4.png"]];
[[CCAnimationCache sharedAnimationCache] addAnimation:dogAnimation name:@"dogAnimation"];
// Kid Animation
CCAnimation *kidAnimation = [CCAnimation animation];
[kidAnimation addSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"kidontrike_1.png"]];
[kidAnimation addSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"kidontrike_2.png"]];
[kidAnimation addSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"kidontrike_3.png"]];
[kidAnimation addSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"kidontrike_4.png"]];
[[CCAnimationCache sharedAnimationCache] addAnimation:kidAnimation name:@"kidAnimation"];
self.isTouchEnabled = YES;
[self scheduleUpdate];
_vehicles = [CCNode node];
[self addChild:_vehicles];
_lives = 9;
_dodges = 0;
double curTime = [[NSDate date] timeIntervalSince1970];
_nextSpawn = curTime + 4;
}
return self;
}
- (void) didLoadFromCCB {
[self setLives:_lives];
[self setDodges:_dodges];
}
- (void) setDodges:(int) noOfDodges {
dodgesLabel.string = [NSString stringWithFormat:@"Dodges:%d", noOfDodges];
}
- (void) setLives:(int) noOfLives {
livesLabel.string = [NSString stringWithFormat:@"Lives:%d", noOfLives];
}
- (void)carDone:(id)sender {
CCSprite *vehicle = (CCSprite *)sender;
[vehicle removeFromParentAndCleanup:YES];
_dodges++;
[self setDodges:_dodges];
}
- (void)doneInvincible {
_invincible = FALSE;
}
- (void)update:(ccTime)dt {
CGSize winSize = [CCDirector sharedDirector].winSize;
CCSprite *vehicleSprite;
// Spawn Vehicles (new)
double curTime = [[NSDate date] timeIntervalSince1970];
if (curTime > _nextSpawn) {
int randomVehicle = arc4random() % 4;
if (randomVehicle == kVehicleTypeRedCar) {
// Red Car
vehicleSprite = [CCSprite spriteWithSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"car1_1.png"]];
[vehicleSprite setUserData:[NSNumber numberWithInt:kVehicleTypeRedCar]];
CCSprite *wheel1 = [CCSprite spriteWithSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"car1_tire.png"]];
CCSprite *wheel2 = [CCSprite spriteWithSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"car1_tire.png"]];
id tireRotateAction1 = [CCRotateBy actionWithDuration:1.0f angle:360.0f]; // Ray, these are backwards on purpose as a lab exercise.
id tireRotateAction2 = [CCRotateBy actionWithDuration:1.0f angle:360.0f];
[wheel1 runAction:[CCRepeatForever actionWithAction:tireRotateAction1]];
[wheel2 runAction:[CCRepeatForever actionWithAction:tireRotateAction2]];
[vehicleSprite addChild:wheel1];
[vehicleSprite addChild:wheel2];
[wheel1 setPosition:ccp(65,18)];
[wheel2 setPosition:ccp(212,18)];
} else if (randomVehicle == kVehicleTypeYellowCar) {
// Yellow Car (Same code as Red Car except for wheel placement, re-listed for clarity. Consilidate in your own games)
vehicleSprite = [CCSprite spriteWithSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"car2_1.png"]];
[vehicleSprite setUserData:[NSNumber numberWithInt:kVehicleTypeYellowCar]];
CCSprite *wheel1 = [CCSprite spriteWithSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"car2_tire.png"]];
CCSprite *wheel2 = [CCSprite spriteWithSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"car2_tire.png"]];
id tireRotateAction1 = [CCRotateBy actionWithDuration:1.0f angle:-360.0f];
id tireRotateAction2 = [CCRotateBy actionWithDuration:1.0f angle:-360.0f];
[wheel1 runAction:[CCRepeatForever actionWithAction:tireRotateAction1]];
[wheel2 runAction:[CCRepeatForever actionWithAction:tireRotateAction2]];
[vehicleSprite addChild:wheel1];
[vehicleSprite addChild:wheel2];
[wheel1 setPosition:ccp(62,15)];
[wheel2 setPosition:ccp(195,15)];
} else if (randomVehicle == kVehicleTypeDog) {
// Dog
vehicleSprite = [CCSprite spriteWithSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"dog_1.png"]];
[vehicleSprite setUserData:[NSNumber numberWithInt:kVehicleTypeDog]];
// In your code, check that the animationByName did not return nil (due to memory warnings)
CCAnimation *vehicleAnimation = [[CCAnimationCache sharedAnimationCache] animationByName:@"dogAnimation"];
vehicleAnimation.restoreOriginalFrame = NO;
vehicleAnimation.delayPerUnit = 0.5f/ vehicleAnimation.frames.count;
id animationAction = [CCAnimate actionWithAnimation:vehicleAnimation];
[vehicleSprite runAction:[CCRepeatForever actionWithAction:animationAction]];
} else {
// Kid on Bike (Same code as Dog, re-listed for clarity. Consilidate in your own games)
vehicleSprite = [CCSprite spriteWithSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"kidontrike_1.png"]];
[vehicleSprite setUserData:[NSNumber numberWithInt:kVehicleTypeKid]];
// In your code, check that the animationByName did not return nil (due to memory warnings)
CCAnimation *vehicleAnimation = [[CCAnimationCache sharedAnimationCache] animationByName:@"kidAnimation"];
vehicleAnimation.restoreOriginalFrame = NO;
vehicleAnimation.delayPerUnit = 0.5f/ vehicleAnimation.frames.count;
id animationAction = [CCAnimate actionWithAnimation:vehicleAnimation];
[vehicleSprite runAction:[CCRepeatForever actionWithAction:animationAction]];
}
// Common placement and movement code for all vehicles
vehicleSprite.position = ccp(winSize.width + vehicleSprite.contentSize.width/2, 75);
[_catJumpBatchNode addChild:vehicleSprite];
[vehicleSprite runAction:[CCSequence actions:
[CCMoveBy actionWithDuration:1.25 position:ccp(-winSize.width-vehicleSprite.contentSize.width, 0)],
[CCCallFuncN actionWithTarget:self selector:@selector(carDone:)],
nil]];
float randomInterval = arc4random() % 3 + 1.5;
_nextSpawn = curTime + randomInterval;
}
// Check for collisions
if (!_invincible) {
float insetAmtX = 10;
float insetAmtY = 10;
BOOL isCatColliding;
CGRect catRect = CGRectInset(cat.boundingBox, insetAmtX, insetAmtY);
CGRect vehicleRect;
for (CCSprite *vehicle in _catJumpBatchNode.children) {
if ([vehicle tag] == 1) {
continue; // No need to check if the Cat collides with itself
}
isCatColliding = NO;
NSNumber *vehicleTypeNumber = (NSNumber*)[vehicle userData];
int vehicleType = [vehicleTypeNumber intValue];
if (vehicleType == kVehicleTypeRedCar) {
CGPoint boundingBoxOrigin = vehicle.boundingBox.origin;
CGRect carHood = CGRectMake(boundingBoxOrigin.x+10,boundingBoxOrigin.y , 40,80);
insetAmtX = 50;
insetAmtY = 10;
vehicleRect = CGRectInset(vehicle.boundingBox,insetAmtX,insetAmtY);
if ((CGRectIntersectsRect(catRect,carHood)) ||
(CGRectIntersectsRect(catRect, vehicleRect))) {
isCatColliding = YES;
CCLOG(@"Collided with Red Car");
}
} else if (vehicleType == kVehicleTypeYellowCar) {
CGPoint boundingBoxOrigin = vehicle.boundingBox.origin;
CGRect carHood = CGRectMake(boundingBoxOrigin.x+10,boundingBoxOrigin.y , 68,65);
insetAmtX = 68;
insetAmtY = 10;
vehicleRect = CGRectInset(vehicle.boundingBox,insetAmtX,insetAmtY);
if ((CGRectIntersectsRect(catRect,carHood)) ||
(CGRectIntersectsRect(catRect, vehicleRect))) {
isCatColliding = YES;
CCLOG(@"Collided with Yellow Car");
}
} else {
// Dog or Kid
CGRect vehicleRect = CGRectInset(vehicle.boundingBox, insetAmtX, insetAmtY);
if (CGRectIntersectsRect(catRect, vehicleRect)) {
isCatColliding = YES;
}
}
if (isCatColliding == YES) {
// Play sound, take a hit, invincible, break out of the loop
[[SimpleAudioEngine sharedEngine] playEffect:@"squish.wav"];
_invincible = TRUE;
[cat runAction:[CCSequence actions:
[CCBlink actionWithDuration:1.0 blinks:6],
[CCCallFunc actionWithTarget:self selector:@selector(doneInvincible)],
nil]];
_lives--;
[self setLives:_lives];
if (_lives <= 0) {
[[CCDirector sharedDirector] replaceScene:[CCTransitionJumpZoom transitionWithDuration:1.0 scene:[CCBReader sceneWithNodeGraphFromFile:@"GameOver.ccbi"]]];
}
break;
}
}
}
}
- (void)doneJump {
_jumping = FALSE;
}
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if (!_jumping) {
_jumping = TRUE;
[[SimpleAudioEngine sharedEngine] playEffect:@"meow.wav"];
CCLOG(@"Making the Cat Jump");
_catJumpAnimation.restoreOriginalFrame = YES;
CCAnimate *jumpAnimation = [CCAnimate actionWithAnimation:_catJumpAnimation];
CCJumpBy *jumpAction = [CCJumpBy actionWithDuration:1.25 position:ccp(0,0) height:200 jumps:1];
CCCallFunc *doneJumpAction = [CCCallFunc actionWithTarget:self selector:@selector(doneJump)];
CCSequence *sequenceAction = [CCSequence actions:jumpAction,doneJumpAction, nil];
[cat runAction:[CCSpawn actions:jumpAnimation,sequenceAction, nil]];
}
}
@end
Не стесняйтесь заглянуть в код, чтобы получить представление о том, что происходит с логикой игрой, но не беспокойтесь об этом слишком много — в центре нашего внимания находится CocosBuilder.
Обратите внимание на переменные в верхней части кода, сразу после оператора #defines:
CCLabelBMFont *livesLabel;
CCLabelBMFont *dodgesLabel;
CCSprite *cat;
Эти переменные, я уверен, что вы их помните, соответствуют переменным, которые вы создали в CocosBuilder. Они автоматически инициализируются как спрайт кота, текст про количество жизней и количество прыжков.
Запустите игру. Теперь, если нажать кнопку Play в главном меню, вы сможете поиграть в игру.
Ура! Вы создали игровую сцену. Это было намного легче, чем уворачиваться от детей на трехколесных велосипедах.
Конец игры
Думаете вы всё сделали? Не совсем.
Вы, наверное, заметили, что когда кот потеряет все жизни, игра выдаёт ошибку. Это потому, что сцена Game Over, которую CCBReader пытается загрузить, отсутствует. Это может быть потому, что вы ее ещё не сделали.
Эта сцена будет иметь две кнопки:
- Main Menu: Возвращение пользователя в главное меню.
- Replay: Пользователь играет в игру снова.
Переключитесь в CocosBuilder и создайте новый файл с именем GameOver. Добавьте две кнопки CCControlButtons с названиями, указанными выше. Убедитесь, что вы не забыли поставить им теги 1 и 2. Кроме того, не забудьте в поле Selector прописать значение buttonPressed:, а в поле Target значение Document root.
Наконец, установите в качестве класса у корневого слоя значение GameOverLayer, сохраните изменения и опубликуйте файл.
Переключитесь обратно в Xcode и добавьте CCBI-файл в проект. Затем создайте новый класс Cocos2D в группе Layers и назовите его GameOverLayer, убедившись, что этот класс – под-класс CCLayer класса.
Добавьте следующие операторы импорта в GameOverLayer.m:
#import "CCControlButton.h"
#import "CCBReader.h"
А также операторы определений:
#define MAIN_MENU_BUTTON_TAG 1
#define PLAY_AGAIN_BUTTON_TAG 2
Добавьте метод, обрабатывающий нажатие кнопки:
-(void)buttonPressed:(id)sender {
CCControlButton *button = (CCControlButton*) sender;
switch (button.tag) {
case MAIN_MENU_BUTTON_TAG:
[[CCDirector sharedDirector] replaceScene:[CCTransitionFlipY transitionWithDuration:1.0 scene:[CCBReader sceneWithNodeGraphFromFile:@"MainMenuScene.ccbi"]]];
break;
case PLAY_AGAIN_BUTTON_TAG:
[[CCDirector sharedDirector] replaceScene:[CCTransitionFadeUp transitionWithDuration:1.0 scene:[CCBReader sceneWithNodeGraphFromFile:@"GameScene.ccbi"]]];
break;
}
}
Запустите программу. Теперь у вас полнофункциональная игра.
Это же было гораздо проще, чем подбор положений спрайтов в коде игры?
Поиск и устранение ошибок в CocosBuilder
CocosBuilder – это отличный инструмент для быстрого и эффективного создания сцен. Однако, этот инструмент не очень хорош в общении, когда что-то не в порядке со сценой. Для того, чтобы сохранить ваше время, я собрал контрольный список, который нужно пройти, в случае, если сцена не работает должным образом или вы получаете неожиданные ошибки.
- Убедитесь, что вы сохранили изменения в CocosBuilder перед публикацией файла. Это очень важно. CocosBuilder не предупреждает вас о том, что у вас есть несохраненные изменения, когда вы запускаете публикацию. Так что сделайте это привычкой — всегда сохранить изменения перед публикацией сцены.
- Когда вы перетаскиваете CCBI-файл в Xcode-проект, всегда проверьте, что в поле Add to target, рядом с вашим проектом стоит галочка. Обычно, как только ставите галочку у проекта в поле Add to targets, она там остаётся. Но не с файлами CCBI. Поэтому, всегда проверяйте наличие этой галочки, особенно если при загрузке сцены у вас появляется ошибка.
- Читайте сообщения в консоли отладки. Если CocosBuilder не может сказать вам, что пошло не так, то консоль отладки может содержать сообщение, которое поможет вам выяснить, что пошло не так. Если там написано: «File not found: GameOver.ccbi», то это значит, что файл GameOver.ccbi или не был добавлен в проект, или не является частью той целевой сборки, которую вы компилируете, или просто есть ошибка в имени файла.
- Убедитесь, что вы не опечатались. Когда вы вводите такой текст, как названия переменных, пользовательских классов или название селекторов событий, очень важно не опечататься. Попробуйте пользоваться копированием через буфер обмена.
Если вы будете использовать эти советы, то вы сэкономите себе время, а если проблема всё же возникнет, то, мы надеемся, вы наброситесь на неё, как резвый кот на мышь.
Что дальше?
Тут весь исходный код окончательного проекта.
Теперь вы готовы использовать CocosBuilder для создания собственных игр. Я надеюсь, что этот урок сэкономит Вам много времени для создания новых игр!
Автор: wildfish