Cocos 2d – кросс-платформенный фреймворк с открытым кодом, для разработки игр и приложений, как браузерных, так и для мобильных платформ. Приложения могут быть запущены на платформах: iOS, Android, Windows Phone, OS X, Windows, Linux.
Главные возможности:
- OpenGL.
- Управление сценами.
- Эффекты смены сцен.
- Спрайты.
- Визуальные эффекты, большое количество качественных 3D эффектов.
- Экшены (движение, масштабирование, повороты).
- Компоненты, позволяющие конструировать UI (меню, кнопки, ползунки, списки, текстовые поля и т. д.).
- Поддержка касаний и акселерометра на мобильных устройствах.
- Поддержка звука, звуковой движок на основе OpenAl.
- Поддержка физических движков Box2D и Chipmunk.
- Тестирование и отладка без необходимости установки на мобильное устройство.
Одна из ветвей фреймворка – Cocos2d JS, позволяет писать игры в JavaScript. Рассмотрим логику и возможности фреймворка на примере создания браузерной игры.
Популярный жанр браузерных игр – головоломки. Целью нашей игры будет расставить в правильном порядке шестеренки, чтобы механизм заработал.
Устанавливаем Cocos 2D
Для установки нам потребуются собственно, сам фреймворк. Можно скачать облегченную html-версию, но если вы хотите воспользоваться всеми возможностями консоли Cocos: создать новый проект, выполнить отладку, скомпилировать релизную версию игры под нужную платформу (мы будем собирать игру под веб и Android) или другие платформы — потребуется полная версия cocos2d-x, и еще ряд инструментов (не забудьте уточнить, какие версии поддерживает Cocos):
- Java JDK (комплект разработки приложений),
- Android SDK (набор инструментов для разработки и отладки приложений),
- Android NDK (это набор инструментов, которые позволяют реализовать часть приложения используя такие языки как С/С++),
- Apache ANT (утилита для автоматизации процесса сборки программного продукта)
- Python.
Сначала необходимо установить Java, затем Android SKD. Далее потребуется запустить Android SKD Manager и установить пакеты для версии Android, с которой предполагается работать. Затем следует Apache ANT и Python.
Установив все необходимые компоненты, распаковываем файлы Cocos, открываем консоль и запускаем инсталлятор setup.py. Он потребует по очереди указать каталоги Android NDK, Android SDK и Apache ANT, после чего станет доступной консоль Сocos. Официальная документация Cocos содержит подробную инструкцию по установке.
Создаем новый проект
Воспользуемся консолью Cocos:
1 |
cocos new GearsGame -l js -d d:\Projects\ |
Здесь мы указываем название будущей игры, используемый язык – JavaScript, и каталог – в который следует поместить проект.
Cocos создаст следующую структуру проекта:
Каталог src – содержит файлы скриптов, res – ваши ассеты. Index.html служит контейнером для игры, рассмотрим его:
В первую очередь следует загрузить необходимые ресурсы:
1 |
<script src="res/loading.js"></script> |
Для отображения игры создается канвас:
1 |
<canvas id="gameCanvas" width="480" height="720"></canvas> |
Следующие строки загружают фреймворк и запускают игру:
1 2 |
<script src="frameworks/cocos2d-html5/CCBoot.js"> </script><script cocos src="main.js"></script> |
Файл project.json содержит базовые настройки: запускать в режиме отладки, показывать данные FPS, установить частоту смены кадров, подключаемые модули и список файлов нашей игры, которые помещаются в каталог src.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
{ "project_type": "javascript", "debugMode" : 1, "showFPS" : true, "frameRate" : 60, "noCache" : false, "id" : "gameCanvas", "renderMode" : 0, "engineDir":"frameworks/cocos2d-html5", "modules" : ["cocos2d"], "jsList" : [ "src/resource.js", "src/app.js" ] } |
Файл main.js содержит код, запускающий игру, здесь можно задать ряд дополнительных настроек:
Ориентацию экрана:
1 |
cc.view.setOrientation(cc.ORIENTATION_PORTRAIT); |
Его разрешение:
1 |
cc.view.setDesignResolutionSize(640, 960, cc.ResolutionPolicy.SHOW_ALL); |
Изменение размеров экрана при изменении размеров окна браузера:
1 |
cc.view.resizeWithBrowserSize(true); |
Ну и наконец:
1 2 3 |
cc.LoaderScene.preload(g_resources, function () { cc.director.runScene(new HelloWorldScene()); }, this); |
Здесь мы загружаем ресурсы, нам потребуются изображения шестеренок и фона.
Поместим их в каталог res, а затем перечислим список в файле resource.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var res = { BG_png : "res/bg.png", GearOrange_png : "res/gear_orange.png", GearViolet_png : "res/gear_violet.png", GearGreen_png : "res/gear_green.png", GearBrown_png : "res/gear_brown.png", GearYellow_png : "res/gear_yellow.png", GearGrey_png : "res/gear_grey.png", GearRed_png : "res/gear_red.png", GearBlue_png : "res/gear_blue.png", GearPink_png : "res/gear_pink.png", Spindle_png : "res/spindle.png" }; |
Все готово и можно запускать игру:
1 |
cc.director.runScene(new HelloWorldScene()); |
Ядром фреймворка являются понятия Scene, Node, Layer, Sprite, Action.
Сцена (Scene) – средство группировки и отображения элементов (узлов) на экране. Она служит контейнером для узлов. Узел (Node) служит строительным материалом игры – слои, меню, надписи, спрайты, визуальные эффекты и т. д.
Если мы откроем файл app.js, то увидим следующее:
1 2 3 4 5 6 7 |
var HelloWorldScene = cc.Scene.extend({ onEnter:function () { this._super(); var layer = new HelloWorldLayer(); this.addChild(layer); } }); |
В качестве примера сюда добавлена сцена, в которую добавлен слой HelloWorldLayer.
Слой (Layer) — контейнер для других узлов, следующий за сценой уровень иерархии Cocos. Слои определяют порядок отрисовки элементов игры (какие из них будут «ближе» к нам) и могут накладываться друг на друга.
Пишем игровой движок
Переименуем сцену в GameScene, и добавим слой для отображения фона:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var BackgroundLayer = cc.Layer.extend({ ctor:function () { this._super(); var size = cc.winSize; this.sprite = new cc.Sprite(res.BG_png); this.sprite.setPosition( size.width * 0.5, size.height * 0.5 ); this.addChild(this.sprite); return true; } }); |
Здесь мы создаем спрайт, который получает наше фоновое изображение и позиционируем его в центре экрана.
Спрайт (Sprite) – это ключевой элементы игры, представленный двухмерным изображением или анимацией, которым, можно манипулировать – передвигать, вращать, изменять размер и пр.
Главным элементом нашей игры будет шестеренка, чтобы она вращалась, ее нужно поместить на ось так, чтобы она вошла в сцепление с другой вращающейся шестерней. Шестеренка может вращаться по часовой стрелке или в обратную сторону, может быть сцеплена с любым количеством шестерней. В игре должна быть одна шестерня, выступающая в качестве двигателя.
Создадим класс для шестерни:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
var GearSprite = cc.Sprite.extend({ ctor:function (img) { this._super(img); this._spindle = null; this._rotateDirection = ROTATE_DIRECTION.none; this._isRotatedGear = false; this._isEngine = false; this._driverGear = null; this._drivenGears = []; } }); |
Направление вращения зададим константой:
1 2 3 4 5 |
var ROTATE_DIRECTION = { none : 0, clockWise : 1, ctrClockWise : 2 }; |
Добавим еще пару констант: зазор для сцепления
1 |
var CLUTCH_GAP = 12; |
и скорость вращения шестерни
1 |
var ROTATE_SPEED = 100; |
Шестерня должна реагировать на действия игрока, вращаться при сцеплении с подвижными шестеренками и останавливаться в противном случае. Для реализации этого прибегнем к механизму планирования событий Cocos.
Планирование – это инструмент, позволяющий конструировать независимый от frame rate геймплей. Для этого добавим в созданный класс функцию update, которая которая будет выполнятся каждый кадр, получая в качестве аргумента дельту времени:
1 2 3 4 5 6 7 8 9 |
update:function (dt) { if(this._rotateDirection == ROTATE_DIRECTION.none) { if(this._isRotatedGear == true) { this.stopAllActions(); for(var i = 0; i < this._drivenGears.length; i++) { this._drivenGears[i].setRotateDirection(ROTATE_DIRECTION.none); } |
Здесь мы проверяем, не изменился ли флаг, указывающий направление вращения шестерни. Если он сброшен, мы останавливаем вращение, для чего используем метод stopAllActions, который останавливает все запущенные экшены спрайта.
Экшены (action) – инструменты, позволяющие манипулировать спрайтами, создавать различные визуальные эффекты.
Затем мы сбрасываем флаги вращения для всех шестерен, которые приводятся в движение текущей. В завершение разрываем сцепление шестерни с другими:
1 2 3 4 5 6 7 8 9 |
var driver = this.getDriverGear(); if(driver) { driver.delDrivenGear(this.getTag()); } this.clearDrivenGears(); this.setDriverGear(null); this._isRotatedGear = false; } |
Если же флаг наоборот, установлен, заставляем шестерню вращаться. Получаем радиус шестеренки и определяем угол поворота по формуле:
1 2 3 4 5 6 |
} else if(this._isRotatedGear == false) { this._isRotatedGear = true; var radius = this.getContentSize().width/2; var angle = ROTATE_SPEED / (2 * Math.PI * radius) * 360; angle = (this._rotateDirection == ROTATE_DIRECTION.clockWise)? angle : -angle; |
После чего создаем action, который будет поворачивать шестерню на заданный угол за секунду:
1 |
var rotate = new cc.RotateBy(1, angle); |
Чтобы анимация не останавливалась, используем следующий прием:
1 |
var repeatForever = new cc.RepeatForever(rotate); |
Запускаем анимацию:
1 |
this.runAction(repeatForever); |
Не забываем передать вращение дальше по цепочке:
1 2 3 4 5 6 7 8 9 |
for(var i = 0; i < this._drivenGears.length; i++) { if(this._rotateDirection == ROTATE_DIRECTION.clockWise) { this._drivenGears[i].setRotateDirection(ROTATE_DIRECTION.ctrClockWise); } else { this._drivenGears[i].setRotateDirection(ROTATE_DIRECTION.clockWise); } } } } |
Чтобы функция update выполнялась, в конструктор класса необходимо добавить инструкцию:
1 |
this.scheduleUpdate(); |
Все работу с шестернями будем производить в отдельном слое:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
var TransmissionLayer = cc.Layer.extend({ ctor:function () { this._super(); this._movingGears = []; this._allGears = []; this._spindles = []; this._engineGear = null; this.createScreen(); this.createListeners(); return true; }, createScreen:function () { this.createSpindles(); this.createGears(); }, |
Сначала разместим оси, на которых будут вращаться шестерни:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
createSpindles:function () { var positions = [ {x : 10, y : 805}, {x : 176, y : 710 }, {x : 356, y : 704 }, {x : 492, y : 578 }, {x : 396, y : 500 }, {x : 375, y : 376 }, {x : 458, y : 264 }, {x : 541, y : 330 }, {x : 218, y : 534 }, {x : 507, y : 453 }, {x : 548, y : 754 }, {x : 112, y : 427 }, {x : 223, y : 278 }, {x : 629, y : 400} ]; for(var i = 0; i < positions.length; i++) { var newSpindle = new cc.Sprite(res.Spindle_png); newSpindle.setPosition(positions[i].x, positions[i].y); newSpindle.setLocalZOrder(-10); this.addChild(newSpindle); this._spindles.push(newSpindle); } }, |
Затем добавим шестерни:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
createGears:function () { var tag = 10; var gearsNames = [ "GearBrown_png", "GearGreen_png", "GearPink_png", "GearViolet_png", "GearYellow_png", "GearRed_png", "GearGrey_png" ]; for(var i = 0; i < gearsNames.length; i++) { var currName = gearsNames[i]; var newGear = new GearSprite(res[currName]); var randX = Math.random() * cc.winSize.width; var randY = (Math.random() * (cc.winSize.height - 200)) + 200; newGear.setPosition(randX, randY); newGear.setTag(tag++); this.addChild(newGear); this._movingGears.push(newGear); this._allGears.push(newGear); } |
Шестеренки, которые игрок должен будет расставить сам, размещаем произвольно, назначая каждой свой тег. После чего добавляем первую и последнюю шестерни, первая будет двигателем:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
var startGear = new GearSprite(res.GearBlue_png); startGear.setEngine(true); startGear.setTag(tag++); startGear.setPosition(this._spindles[0].getPosition()); startGear.setSpindle(this._spindles[0]); startGear.setRotateDirection(ROTATE_DIRECTION.clockWise); this.addChild(startGear); this._engineGear = startGear; this._allGears.push(startGear); var lastSpindle = this._spindles[this._spindles.length-1]; var finalGear = new GearSprite(res.GearOrange_png); finalGear.setTag(tag++); finalGear.setPosition(lastSpindle.getPosition()); finalGear.setSpindle(lastSpindle); this.addChild(finalGear); this._allGears.push(finalGear); }, |
Мы не добавляем их в коллекцию movingGears, чтобы игрок не мог их перемещать.
Реакцию на действия пользователя обеспечивает диспетчер событий Cocos. Он поддерживает события экрана мобильного устройства: касания (в том числе мультитатч) и события акселератора. Так же доступны события мыши и клавиатуры.
Игрок должен иметь возможность перетаскивать шестерни с места на место, для этого создадим следующие слушатели:
1 2 3 4 5 6 7 8 9 10 11 12 |
createListeners:function () { var self = this; var movedGear = null; cc.eventManager.addListener({ event: cc.EventListener.TOUCH_ONE_BY_ONE, swallowTouches : true, onTouchBegan: onTouchBegan, onTouchMoved: onTouchMoved, onTouchEnded: onTouchEnded }, this); |
Здесь мы указываем, что одновременно может обрабатываться только одно касание, все другие игнорируются. Добавим обработчики событий:
1 |
function onTouchBegan(touch, event) { |
Получаем координаты касания:
1 |
var tap = touch.getLocation(); |
И если оно приходится на одну из шестеренок, останавливаем ее вращение, снимаем с оси и выводим на передний план задав свойство zOrder равным 0.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
for(var i = 0; i < self._movingGears.length; i++) { var currGear = self._movingGears[i]; var gearRect = currGear.getBoundingBox(); if(cc.rectContainsPoint(gearRect, tap)) { movedGear = currGear; movedGear.setLocalZOrder(0); movedGear.setRotateDirection(ROTATE_DIRECTION.none); movedGear.setSpindle(null); return true; } } return false; } |
Здесь мы с помощью метода getBoundingBox получаем занимаемую шестерней область в виде прямоугольной рамки. Метод возвращает объект, содержащий координаты начала этой области и ее размеры. Однако, из-за вращения шестерни, область будет рассчитываться неверно. Поэтому переопределим для шестерни метод getBoundingBox следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
getBoundingBox:function () { var size = this.getContentSize(); size.width *= 0.8; size.height *= 0.8; return { x: this.getPositionX() - size.width * 0.5, y: this.getPositionY() - size.height * 0.5, width: size.width, height: size.height } } |
Вернемся к обработчику onTouchBegan. Используем метод rectContainsPoint, который получает область, занимаемую спрайтом, и точку на экране, возвращая true, в случае если точка находится в границах области.
Далее, передвигаем шестерню, задавая ей координаты касания:
1 2 3 4 5 |
function onTouchMoved(touch, event) { var tap = touch.getLocation(); movedGear.setPosition(tap); } |
И в тот момент, когда игрок отпускает ее, проверяем, находится ли центр шестерни над осью.
1 2 3 4 5 6 |
function onTouchEnded(touch, event) { for(var i = 0; i < self._spindles.length; i++) { var currSpindle = self._spindles[i]; var rectSpindle = currSpindle.getBoundingBox(); var gearPos = movedGear.getPosition(); |
В этом случае сажаем шестерню на ось и проверяем, вступила ли она в сцепление с другими шестернями:
1 2 3 4 5 6 7 8 9 10 |
if(cc.rectContainsPoint(rectSpindle, gearPos)) { movedGear.setPosition(currSpindle.getPosition()); movedGear.setSpindle(currSpindle); self.checkClutch(); } } } }, |
Мы проверяем все шестерни по очереди их сцепления, начиная с первой:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
checkClutch:function () { var self = this; var checkedGears = []; checkedGears.push(this._engineGear.getTag()); clutchGears(this._engineGear); function clutchGears(engineGear) { for(var i = 0; i < self._allGears.length; i++) { var currGear = self._allGears[i]; if(checkedGears.indexOf(currGear.getTag()) < 0 && engineGear.getTag() != currGear.getTag() && currGear.getSpindle() != null) { |
Сначала получаем дистанцию между центрами двух шестеренок. Определяем разницу координат по обеим осям:
1 2 3 4 5 |
var enginePos = engineGear.getPosition(); var currGearPos = currGear.getPosition(); var diffX = Math.abs(enginePos.x - currGearPos.x); var diffY = Math.abs(enginePos.y - currGearPos.y); |
и применяем теорему Пифагора:
1 |
var distance = Math.sqrt(Math.pow(diffX, 2) + Math.pow(diffY, 2)); |
затем получаем радиусы шестеренок, они должны пересекаться в пределах заданного константой CLUTCH_GAP зазора, если это так, заставляем шестерню вращаться:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
var engineR = engineGear.getContentSize().width/2; var rectCurrGear = currGear.getContentSize(); var maxR = rectCurrGear.width/2; var minR = maxR - CLUTCH_GAP; if(distance > (engineR + minR) && distance < (engineR + maxR)) { engineGear.addDrivenGear(currGear); if(engineGear.getRotateDirection() == ROTATE_DIRECTION.clockWise) { currGear.setRotateDirection(ROTATE_DIRECTION.ctrClockWise); } else { currGear.setRotateDirection(ROTATE_DIRECTION.clockWise); } currGear.setDriverGear(engineGear); checkedGears.push(currGear.getTag()); clutchGears(currGear); } } } } } |
Добавляем интерфейс пользователя
Cocos2d так же предоставляет коллекцию компонентов для создания пользовательского интерфейса: кнопки, переключатели, ползунки, текстовые поля, надписи. Они содержатся в модуле ccui, который должен быть подключен в файле project.json. Добавим на экран пару кнопок, с помощью которых игрок сможет начать игру заново или завершить ее.
Создадим класс кнопки:
1 2 3 4 5 6 7 8 |
var Button = ccui.Button.extend({ ctor:function (name, caption, callback) { this._super(); this.createScreen(name, caption); this.createListeners(callback) }, createScreen:function (name, caption) { |
Кнопка использует два изображения для анимации нажатия:
1 2 |
this.loadTextures(res.ButtonNormal_png, res.ButtonSelected_png); this.setName(name); |
Надписи могут создаваться на основе текстурного атласа, файла BMFONT или с использованием стандартного шрифта (TTF файл). Добавим надпись для кнопки, используя компонент LabelTTF. Потребуется указать текст надписи, используемый шрифт и его размер. Надпись поместим по центру кнопки.
1 2 3 4 5 6 |
var size = this.getContentSize(); var captionLabel = new cc.LabelTTF(caption, "Arial", 48); captionLabel.setPosition(size.width * 0.5, size.height * 0.5); this.addChild(captionLabel); }, |
Так же добавим обработчик события кнопки:
1 2 3 4 5 6 7 8 9 10 11 |
createListeners:function (callback) { this.addTouchEventListener(this.touchEvent(callback), this); }, touchEvent:function (callback) { return function (sender, type) { if(type == ccui.Widget.TOUCH_ENDED) { callback(); } }; } }); |
Интерфейс пользователя поместим в отдельный слой:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
var UILayer = cc.Layer.extend({ ctor:function () { this._super(); this.createScreen(); }, createScreen:function () { var size = cc.winSize; var exitButton = new Button("Close", "Меню", function () { cc.director.runScene(new MenuScene); }); var buttonSize = exitButton.getContentSize(); exitButton.setPosition(size.width - buttonSize.width * 0.5, buttonSize.height * 0.5); this.addChild(exitButton); var restartButton = new Button("Restart", "Рестарт", function () { cc.director.runScene(new cc.TransitionShrinkGrow( 2.0, new GameScene() )); }); restartButton.setPosition(buttonSize.width * 0.5, buttonSize.height * 0.5); this.addChild(restartButton); } }); |
В итоге сцена игры выглядит следующим образом:
1 2 3 4 5 6 7 8 9 |
var GameScene = cc.Scene.extend({ onEnter:function () { this._super(); this.addChild(new BackgroundLayer(), 0); this.addChild(new TransmissionLayer(), 1); this.addChild(new UILayer(), 2); } }); |
Контроль над всеми процессами в игре осуществляет Director. Он управляет сменой сцен, эффектами перехода. Чтобы продемонстрировать переключение между сценами добавим меню пользователя. Оно будет состоять всего из одного пункта: Старт. Создадим класс пункта меню:
1 2 |
var MenuItem = cc.MenuItemSprite.extend({ ctor:function (caption, callback) { |
Так же как и с кнопкой, нам требуется два изображения для анимации выбора
1 2 3 4 |
var menuNormal = new cc.Sprite(res.MenuItem_png); var menuSelected = new cc.Sprite(res.MenuItem_png); this._super(menuNormal, menuSelected, null, this.onEvent(callback), this); |
Мы можем увеличить размер изображения с помощью свойства Scale:
1 |
this.setScale(1.5); |
Добавим надпись:
1 2 3 4 5 6 7 |
var size = this.getContentSize(); var playLabel = new cc.LabelTTF(caption, "Arial", 32); playLabel.setPosition(size.width * 0.5, size.height * 0.5); this.addChild(playLabel); }, |
И обработчик события:
1 2 3 4 5 6 7 8 9 10 11 |
onEvent:function (callback) { return function () { var scale = new cc.ScaleBy(0.5, 1.5); var easeScale = new cc.EaseBackIn (scale); var cbFunc = new cc.callFunc(callback); var secuence = new cc.Sequence(easeScale, cbFunc); this.runAction(secuence); } } }); |
Здесь мы добавили визуальный эффект, который будет увеличивать размер элемента меню при касании. Для создания более плавных и сложных эффектов можно прибегнуть к Actions Easing. Они анимируют спрайт с помощью специальных функций, дающих эффект ускорения, замедления, колебания и т. д. За основу берется один из простых actions, в нашем случае ScaleBy, который увеличивает размер элемента относительно текущего.
По завершении анимации должна открываться следующая сцена, для этого используем специальный объект Sequence, который позволяет задать очередь из экшенов и выполнить их последовательно.
Добавим для меню отдельную сцену с двумя слоями, один для фонового изображения, и один для элементов интерфейса:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
var MenuScene = cc.Scene.extend({ onEnter:function () { this._super(); this.addChild(new BackgroundLayer()); this.addChild(new MenuLayer()); } }); var MenuLayer = cc.Layer.extend({ ctor:function () { this._super(); this.createScreen(); }, createScreen:function () { var size = cc.winSize; var playItem = new MenuItem("Старт", function () { cc.director.runScene(new cc.TransitionShrinkGrow( 2.0, new GameScene() )); }); this.mainMenu = new cc.Menu(playItem); this.mainMenu.setPosition(size.width * 0.5, size.height * 0.5); this.addChild(this.mainMenu); } }); |
Director позволяет выполнить запуск сцены с эффектом перехода, мы воспользовались одним из них для запуска сцены с игрой:
1 |
cc.director.runScene(new cc.TransitionShrinkGrow( 2.0, new GameScene() )); |
Публикуем HTML5 игру и создаем apk для Android
Игра готова, осталось только построить ее релизную версию, для этого воспользуемся консолью cocos:
1 |
cocos compile -p web -s d:\projects\gearsGame\ -m release |
Мы указываем платформу Web и каталог проекта. Компилятор выполнит сжатие проекта и поместит весь JavaScript-код в файл game.min.js. Скомпилированные файлы будут помещены в подкаталог publish/html5.
Cocos 2d – позволяет скомпилировать игру и на другие поддерживаемые платформы. Для запуска игры на устройстве с ОС Android выполним компиляцию проекта следующим образом:
1 |
cocos compile -p android -ap android-19 -s d:\Projects\gearsGame\ |
Отсутствие параметра -m release будет означать, что создается debug-версия. Скомпилированный apk файл игры будет помещен в подкаталог simulator.
Заключение
В данной статье продемонстрирован пример разработки игры с использованием Cocos 2d-JS, который дает общее представление о логике работы с этим движком. Большое количество примеров в сети, простой и удобный API, а также большее сообщество помогут начинающим быстро освоить фреймворк и приступить к созданию своих игр. Ресурсы, исходный код и релизные версии для платформ Web, Android и Windows можно скачать с репозитория GitHub. Web версия игры доступна по этой ссылке.