Предисловие
Начну с небольшой предыстории. Не так давно, мы с другом решили разработать свою двумерную игру, для дипломного проекта. После того как мы определились с ее жанром и получили примерное представление об игровом процессе, перед нами встал вопрос о выборе движка. Unreal или Uniti мы отбросили сразу, так как они показались нам слишком «тяжелыми» инструментами для 2d игры. Cocos2d-x был бы идеальным вариантом, если бы не одна деталь — практически полное отсутствие вводных уроков. Те статьи, что нам удалось найти были, либо не законченны, либо недостаточно подробны. И тогда, я решил перевести официальную документацию, практически без знаний английского и, за одно, поделится своим переводом с остальными (кто же если не я).
Эта статья представляет собой почти дословный перевод, официальной документации к Cocos2d-x. Если вы не хотите разбираться во всяких тонкостях устройства движка, а хотите сразу начать писать свою игру, могу посоветовать вам эту статью: Cocos2d-x — разработка простой игры
Если вы еще не установили Cocos2d-x или не можете создать проект, то тут вы найдете подробную инструкцию: Создание многоплатформенных игр с использованием Cocos2d-x версии 3.0 и выше
Оригинальная статья: Cocos2d-x programmers guide
P.S.: Не советую клонировать cocos2d-x с gitHub. У меня после клонирования не доставало нескольких важных файлов. Но может быть мне просто не повезло.
Приступим!
Основные понятия Cocos2d-x
Эта глава предполагает, что вы только начали ознакомление с Cocos2d-x, и уже готовы начать работу над игрой вашей мечты. Не волнуйтесь, будет весело!
Cocos2d-x кросс-платформенный игровой движок. Игровой движок — это программное обеспечение, которое обеспечивает базовый функционал, необходимый всем играм. Вы могли слышать, что это называется API или фреймворк, но в этом руководстве мы будем называть его 'игровым движком'.
Игровые движки включают множество компонентов, которые при совместном использовании повышают скорость разработки, и часто работают лучше самодельных библиотек. Игровой движок обычно содержит некоторые или все следующие компоненты: визуализатор, 2d/3d графику, обработчик столкновений, физический движок, звук, анимацию и многое другое. Игровые движки обычно поддерживают несколько платформ, таким образом они упрощают портирование вашей игры на различные устройства.
Поскольку Cocos2d-x является игровым движком, он предоствляет API для разработки кросс-платформенных игр. Благодаря инкапсуляции мощности внутри простого для использования API, вы можете сосредоточится на разработке вашей игры и меньше беспокоиться о технической основе. Cocos2d-x возьмет на себя тяжелую работу.
Cocos2d-x предоставляет следующие объекты: Scene, Transition, Sprite, Menu, Sprite3D, Audio и многие другие. Всё, необходимое для создания игры включено.
Main Components
Это может показаться очень сложным, но начать использовать Cocos2d-x просто. Перед погружением, мы должны понять некоторые концепции используемые в Cocos2d-x. В сердце Cocos2d-x лежат объекты: Scene, Node, Sprite, Menu и Action. Посмотрите на любую игру и вы увидите все эти компоненты в той или иной форме!
Взгляните сюда. Возможно вы обнаружите схожесть с одной очень популярной игрой:
Давайте посмотрим еще раз, но разделим скриншот на компоненты, используемые для его создания:
Вы можете видеть меню, некоторые спрайты и надписи, все они имеют аналоги в Cocos2d-x. Взгляните на свой собственный концепт игры и посмотрите какие у вас есть компоненты, скорее всего вы обнаружите совпадения.
Director
Cocos2d-x использует концепцию режиссера (Director), как в кино! Объект Director контролирует поток операций и сообщает всем что делать. Основной задачей режиссера является контроль переходов и замен сцен. Director — это базовый singleton (фактически, может существовать только один экземпляр этого класса за раз), который может быть вызван в любом месте вашего кода.
Вот пример типичного игрового потока. Director позаботится о его исполнении, в соответствии критериям вашей игры:
Вы являетесь режиссером вашей игры. Вы решаете что, когда и каким образом происходит.
Scene
В своей игре вы, вероятно, хотите видеть главное меню, несколько уровней и завершающую сцену. Как вы разобьете всё это организуете? Как вы догадались, при помощи сцен (Scene). Вспомните какой-нибудь понравившийся вам фильм. Вы можете заметить, что он четко разбит на сцены или отдельные части истории. Если мы применим тот же самый подход к играм, мы должны придумать как минимум несколько сцен, независимо от того, насколько простая игра.
Снова взглянем на уже знакомое нам изображение:
Это главное меню и оно является тодельной сценой. Эта сцена состоит из нескольких частей, которые в совокупности дают нам финальный результат. Сцены рисуются при рендере. Рендер отвечает за отображение спрайтов и других объектов на сцене. Чтобы лучше это понять нам нужно немного поговорить о графе сцены (scene graph).
Scene Graph
Scene graph это структура данных организующая графическую сцену. Scene graph содержит Node объекты в древовидной (да, это называется графом, но, фактически, представляет собой дерево) структуре.
Я уверен, что вы задаётесь вопросом, почему вы должны думать об этих технических деталях, если Cocos2d-x делает тяжёлую работу за вас? Это действительно важно для понимания того, как сцены отрисовываются в ходе рендера.
Как только вы начинаете добавлять узлы, спрайты и анимации в свою игру, вы хотите быть уверены в том, что на экране отобразится именно то, чего вы ожидали. Но что если этого не произошло? Что если ваши спрайты оказались скрыты за фоном, а вы хотите, чтобы они были спереди? Ничего страшного, просто сделайте шаг назад и пробегитесь по графу сцены на листе бумаги, и я уверен, вы легко найдете свою ошибку.
Поскольку Scene Graph является деревом; вы можете его обойти. Cocos2d-x использует in-order алгоритм обхода. Вышеупомянутый алгоритм in-order начинает обход с левой стороны дерева, затем проходит через корень и завершается в правой части дерева. Поскольку правая сторона дерева рендерится последней, на экране она отображается первой.
Граф сцены легко продемонстрировать, давайте взглянем на нашу игровую сцену:
Она будет отображаться на экране согласно дереву, приведенному ниже:
Еще один момент о котором нужно рассказать — элементы с отрицательным порядком Z (z-order) находятся с левой стороны дерева, в то время как элементы с положительным порядком Z находятся с правой стороны. Держите это во внимании при упорядочивании ваших элементов! Конечно, вы можете добавлять элементы в любом порядке, и они автоматически отсортируются исходя из настраиваемого параметра z-order.
Основываясь на этой концепции, мы можем подумать о сцене как о коллекции узлов (Node). Давайте разобьем сцену на части с верху вниз, чтобы увидеть как scene graph использует z-order для компоновки сцены:
Фактически, сцена собрана из множества узлов, с различными параметрами z-order, сложенных в «стек» друг над другом.
В Cocos2d-x вы строите scene graph используя вызов addChild():
// Добавление дочернего элемента с z-order равным -2, таким образом
// он идет в левую сторону дерева (потому что z-order отрицательный)
scene->addChild(title_node, -2);
// Если вы не указали z-order, будет использован 0
scene->addChild(label_node);
// Добавление дочернего элемента с z-order равным -2, таким образом
// он пойдет в правую часть дерева (потому что z-order положительный)
scene->addChild(sprite_node, 1);
Sprites
Все игры содержат спрайты, и вы, наверняка, уже имеете представление о них. Спрайты представляют собой графические объекты, которые вы перемещаете по экрану. Главный персонаж вашей игры, вероятнее всего, тоже спрайт. Я знаю, вы можете спросить — не каждый графический объект является спрайтом? Нет! Почему? Спрайт является спрайтом, только если вы его перемещаете. Если вы не перемещаете его, то это обычный узел (Node).
Ещё раз взглянем на знакомое изображение, отметим где спрайты а где узлы:
Спрайты важны в каждой игре. При написании платформера, у вас, вероятно, есть главный персонаж, который был создан с использованием какого-либо изображения. Это и есть спрайт.
Спрайты просто создать и они имеют следующие настраиваемые параметры: position, rotation, scale, opacity, color и другие.
// Вот так создается спрайт
auto mySprite = Sprite::create("mysprite.png");
// А так изменяются свойства спрайта
mySprite->setPosition(Vec2(500, 0));
mySprite->setRotation(40);
mySprite->setScale(2.0); // равномерно задает масштаб обеим осям X и Y
mySprite->setAnchorPoint(Vec2(0, 0));
Давайте продемонстрируем каждое свойство из данного кода, рассмотрим следующий скриншот:
Если мы зададим позицию используя mySprite->setPositon(Vec2(500, 0));:
Узел-спрайт изменил свою позицию с оригинальной на заданную нами.
Если мы сейчас зададим вращение, используя mySprite->setRotation(40);:
… вы можете видеть, что спрайт был повернут на величину, которая была указанна.
Если мы сейчас зададим новый масштаб используя mySprite->setScale(2. 0);:
Снова, мы можем видеть, что спрайт изменился в соответствии с кодом.
Наконец, все узлы (поскольку Sprite является подклассом Node) имеют якорную точку (anchor point). Мы еще не говорили об этом, так что сейчас самое время. Вы можете думать о якорной точке как о способе определения, какая часть спрайта будет использоваться как базовая координата, при задании позиции.
Зададим якорную точку для персонажа нашей игры, используя следующий вызов:
mySprite->setAnchorPoint(Vec2(0, 0));
Это приведет к тому, что нижний левый угол нашего спрайта станет использоваться как базис для каждого вызова setPosition(). Давайте взглянем на несколько таких примеров в действии:
Обратите внимание на красную точку в каждом изображении. Эта красная метка иллюстрирует, где находится якорная точка!
Как вы могли заметить, якорная точка очень полезна при позиционировании узлов. Вы даже можете регулировать якорную точку динамически, для симуляции эффектов в вашей игре.
Actions
Создание сцены и добавление спрайтов на экран — это только часть того, что нам необходимо сделать. Чтобы сделать игру, нам необходимо заставить вещи двигаться! Action объекты являются основной частью каждой игры. Действия (Action) позволяют трансформировать Node объекты во времени. Хотите двигать спрайт из одной точки в другую? Нет проблем! Вы даже можете создать последовательность (Sequence) действий, которые будут выполнены на узле. Вы можете менять параметры узла такие как position, rotation и scale. Примеры действий: MoveBy, Rotate, Scale.
Сейчас мы продемонстрируем работу действий:
… и через 5 секунд спрайт переместится в новое положение:
Action объекты создавать легко:
auto mySprite = Sprite::create("Blue_Front1.png");
// Двигаем спрайт на 50 пикселей вправо и 10 пикселей вверх за 2 секунды
auto moveBy = MoveBy::create(2, Vec2(50,10));
mySprite->runAction(moveBy);
// Двигаем спрайт в заданную локацию в течении двух секунд.
auto moveTo = MoveTo::create(2, Vec2(50,10));
mySprite->runAction(moveTo);
Sequence и Spawn
С движущимися по экрану спрайтами мы имеем все необходимое, для создания нашей игры, верно? Не вполне. Как же запустить несколько действий? Cocos2d-x реализует это, несколькими путями.
Sequence — это множество действий, запускаемых в определенном прядке. Нужно запустить последовательность в обратном порядке? Нет проблем, Cocos2d-x реализует это без дополнительной работы.
Взглянем на следующий пример последовательности для постепенного движения спрайта:
Эту последовательность легко создать:
auto mySprite = Node::create();
// двигается в точку 50, 10 за 2 секунды
auto moveTo1 = MoveTo::create(2, Vec2(50,10));
// сдвигается относительно текущей позиции на 100, 10 за 2 секунды
auto moveBy1 = MoveBy::create(2, Vec2(100,10));
// двигается в точку 150, 10 за 2 секунды
auto moveTo2 = MoveTo::create(2, Vec2(150,10));
// создаем задержку
auto delay = DelayTime::create(1);
mySprite->runAction(Sequence::create(moveTo1, delay, moveBy1, delay.clone(),
moveTo2, nullptr));
Этот пример запускает последовательность, по порядку, но как запустить все указанные действия одновременно? Такая возможность тоже есть в Cocos2d-x, это называется Spawn. Spawn будет принимать все указанные действия и запускать их одновременно. Некоторые могут быть длиннее других, в таком случае все они не будут закончены в одно время.
auto myNode = Node::create();
auto moveTo1 = MoveTo::create(2, Vec2(50,10));
auto moveBy1 = MoveBy::create(2, Vec2(100,10));
auto moveTo2 = MoveTo::create(2, Vec2(150,10));
myNode->runAction(Spawn::create(moveTo1, moveBy1, moveTo2, nullptr));
Родители, дети и их связь
Cocos2d-x использует принцип наследования свойств родителей и детей. Это означает, что изменения родительского узла применяются и к дочерним элементам. Рассмотрим один спрайт, который имеет наследников:
Изменение вращения родителя так же изменяет вращение всех детей:
auto myNode = Node::create();
// задаем вращение
myNode->setRotation(50);
По аналогии с вращением, если вы измените масштаб родителя, наследникам так же будет задан новый масштаб:
auto myNode = Node::create();
// масштабирование
myNode->setScale(2.0); // увеличиваем в 2 раза
Не все изменения родителя отражются на детях. Изменения родительской якорной точки влияет только на операции трансформации (scale, position, rotate, skew, etc ...) и не влияет на позиционирование наследников. Фактически, дети всегда будут добовлятся в нижний-левый (0,0) угол родителя.
Logining как способ вывода сообщений
Иногда, когда ваше приложение запущено, вы можете захотеть увидеть сообщение, выведенное в консоль, для информации или отладочных целей. Реализуется движком, с помощью использования log().
// простая строка
log("This would be outputted to the console");
// переменная string
string s = "My variable";
log("string is %s", s);
// double
double dd = 42;
log("double is %f", dd);
// integer
int i = 6;
log("integer is %d", i);
// float
float f = 2.0f;
log("float is %f", f);
// bool
bool b = true;
if (b == true)
log("bool is true");
else
log("bool is false");
Если вы предпочитаете std::cout, то можете использовать его вместо log(), однако log() мог бы предоставить упрощенное форматирование сложного вывода.
Вывод
Мы разобрали множество понятий Cocos2d-x. Можете глубоко вздохнуть. Не волнуйтесь. Просто погрузитесь в свои идеи и реализуйте их, шаг за шагом. Cocos2d-x и программирование не являются навыками, которые изучаются за одну ночь. Они требуют практики и понимания. Помните, что форумы так же помогут ответить на ваши вопросы.
От переводчика
Возможно, многое из этой статьи вам уже было известно. Но это была лишь вводная часть из серии уроков по cocos2d-x. В дальнейшем, я буду продолжать переводить новые статьи и выкладывать их сюда, если это будет кому-то интересно.
Автор: MrMysterious