Доброго времени суток.
Начнем с того, что я не нашел на хабре туториалов по Cocos2d и Lua, поэтому мне пришлось много страдать и чтобы вы не повторяли моих ошибок я решил написать пост. В этой статье я расскажу как создать простую игру используя Сocos2d-x, Cocos Code IDE и Lua. Ранее, я уже писал про создание игр на Love2d. В этой статье я адаптирую старый туториал для кокоса и как это запустить на андроиде (Ни яблока, ни мака у меня нету).
Что нам понадобится?
Список важных вещей:
- Java JDK
- Сам Cocos2d-x. На момент выхода этой статьи вышел Cocos2d-x 3.2 и скачать его можно здесь. Нужно скачать той же разрядности, что и JDK
- IDE для lua. Разработчики движка постарались и выпустили свой IDE на основе Eclipse. Качаем здесь.
- [Windows-only]Python 2.7. Лежит вот тут.
Если вы хотите писать игры под андроид, то придется скачать еще пару вещей, а именно:
- Android SDK. Если вы не собираетесь писать под андроид, то лучше найти вкладку «GET THE SDK FOR AN EXISTING IDE»
- Android NDK. Я использую Android NDK r9, а с r10 возможны проблемы
- Apache Ant. У меня стоит 1.9.4
Установка
Сначала поставим Java JDK и Python. После распакуйте cocos2d-x и Cocos Code IDE (Если вы скачивали zip версию, если нет, просто запустите установку) в удобное для вас место. Распаковываем Android SDK, NDK и Apache Ant так же в удобное место. Путь к ним не должен содержать пробелов во избежание проблем. Открываем папку с SDK, находим SDK Manager. Запускаем и ставим нужные для нас версии API и всю папку Tools.
Запускаем Cocos Code IDE. В Window->Preferences->Cocos указываем нудные пути. У меня это выглядит так:
.
Теперь кликаем по вкладке Lua которая находится во вкладке Cocos и выбираем путь к cocos2d-x:
Настроено.
Немного теории
Cocos2d-x — кроссплатформенный движок, написанный на С++, был создан как копия Cocos2d. Cocos2d использует Object C и может использоваться только для iOS.
Теперь поговорим о базовой механике движка и сравним ее с Love2D. У кокоса все объекты находятся в слоях, а слои находятся в сценах. В Love2D ни слоев, ни сцен нету. Плюсы сцен и слоев в том, что с их помощью можно создавать гибкое приложение более простыми способами. Минусы в том, что для просто игры это может быть слишком громоздко и неудобно. В кокосе все представленно объектами и чтобы что-то появилось на экране надо просто добавить это на сцену, а в love2d все представленно простыми переменными и чтобы что-то появилось на экране нужно это каждый кадр рисовать. Это можно описывать достаточно долго поэтому я скажу вывод сейчас, а разницу вы сможете почувствовать сами. Cocos2d-x — большой движок с большим количеством возможностей, однако доступ к ним часто сделан не так как хотелось бы. Love2d — простой, не требующий много знаний, движок с отзывчивым коммьюнити, но с отсутствием многих нужных вещей, которые впоследствии пишут сами пользователи, но делают это вполне удачно.
А теперь сам код
Создадим новый проект. И у нас будет два файла в исходниках: hello2.lua и main.lua. Если вы не хотите посмотреть на чудо китайского гейм дева, то первый можно сразу удалять, а второй очистить. Он создан лоя демонстрации возможностей require. Открываем main.lua и видим кучу кода которая нужна чтобы понять как использовать порт под lua. Чтобы увидеть, что это такое нажимаем F11. Можно поиграться. Вот это бегающие существо это собака, а не белка как вы могли подумать. Посмотрели, поигрались и хватит. Удалите hello2.lua и весь код в main.lua и очизаем папку res. В папку res добавляем картинку и называем ее habr.png. Теперь пишем в main следующий код:
--Все классы кокоса
require "Cocos2d"
--Все константы кокоса
require "Cocos2dConstants"
-- Функция вывода сообщений
cclog = function(...)
print(string.format(...))
end
-- Эта функция выводит ошибки
function __G__TRACKBACK__(msg)
cclog("n----------------------------------------")
cclog("LUA ERROR: " .. tostring(msg))
cclog(debug.traceback())
cclog("----------------------------------------")
return msg
end
collectgarbage("collect")
-- Предотвращает утечку памяти
collectgarbage("setpause", 100)
collectgarbage("setstepmul", 5000)
--Теперь можно использовать файлы которые лежат в папках "src" и "res"
cc.FileUtils:getInstance():addSearchResolutionsOrder("src");
cc.FileUtils:getInstance():addSearchResolutionsOrder("res");
--Записываем в константы гирину и высоту экрана
SCREEN_WIDTH = cc.Director:getInstance():getWinSize().width
SCREEN_HEIGHT = cc.Director:getInstance():getWinSize().height
local function main()
print("Resolution: " .. SCREEN_WIDTH .. "x" .. SCREEN_HEIGHT)
--Запускаем главную сцену
require "mainScene.lua"
end
--Вызываем функцию main
local status, msg = xpcall(main, __G__TRACKBACK__)
if not status then
error(msg)
end
В функции main мы вызываем файл mainScene.lua. В нем находится главная сцена игры. Создадим его и напишем туда следующий код:
-- Константы
local NONE = 0
local ROTATION = 1
local SCALLING = 2
local MOVING = 3
-- Переменные
local state, rotation, scale, ox, oy, delta, habrImage, moving
-- Эта функция будет вызываться когда палец касается экрана
local function onTouchBegan(touch, event)
-- Включаем следующие состояние
state = (state + 1) % 4
-- И возвращаем переменные к дефолту
resetVariables()
end
-- Эта функция вызывается постоянно, а в dt хранится время с
-- прошлого вызова. Если dt == 1.0, то с прошлого вызова прошла
-- одна секунда
local function update(dt)
if state == ROTATION then
--крутим картинку
rotation = rotation + delta * dt
elseif state == SCALLING then
--увеличиваем
scale = scale + dt * delta
elseif state == MOVING then
--Здесь немного посложнее, но все же просто:
--Каждый раз мы увеличивыем переменную moving, а
--потом берем ее за угол для косинису и синуса
--и крутим картинку по кругу
moving = moving + delta * dt
local radius = 50
ox = radius * math.sin(moving)
oy = radius * math.cos(moving)
end
-- В love2d мы использовали эти параметры при рисовании
-- Здесь у нас спрайт и нам нужно постоянно менять ему
-- все вручную
-- Меняем размер
habrImage:setScale(scale)
-- Меняем угол
habrImage:setRotation(rotation)
-- Меняем позицию
habrImage:setPosition(cc.p(SCREEN_WIDTH / 2 + ox, SCREEN_HEIGHT / 2 + oy))
end
local function init()
print("Creating main scene")
-- Создаем сцену
local mainScene = cc.Scene:create()
-- Создаем слой с фоновым цветом
-- Здесь я бы хотел поставить паузу и рассказать более подробно
-- потому что у меня ушло 2 часа чтобы узнать как мне изменить цвет
-- фона. 1) В C++ нету функции cc.c4b(r,g,b,a) поэтому мне пришлось
-- догадаться, что нужно смотреть не на C++ таких случаях, а на
-- cocos2d-x-js, т.к. он более популярен чем cocos2d-x-lua
-- и если вы не можете найти нужную функцию в C++, то ищите ее в
-- JavaScript версии, однако это не поможет с евентами
local gameLayer = cc.LayerColor:create(cc.c4b(255, 255, 255, 255))
-- Создаем спрайт
habrImage = cc.Sprite:create("res/habr.png")
--Ставим его смещение по центру
habrImage:setAnchorPoint(0.5, 0.5)
--Ставим позицию на центр экрана
habrImage:setPosition(cc.p(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2))
--Добавляем его к слою
gameLayer:addChild(habrImage)
state = 0
resetVariables()
-- Чтобы функция update вызывалась нужно сообщить кокосу, что ее нужно
-- вызывать
cc.Director:getInstance():getScheduler():scheduleScriptFunc(update, 0, false)
-- Создаем лисениры для событий. Хочу обратить внимание, что для каждого
-- типа событий есть свой лисенер.
local listener = cc.EventListenerTouchOneByOne:create()
-- Говорим лисениру, что такой то тип событий связвн с такой то функцией
listener:registerScriptHandler(onTouchBegan, cc.Handler.EVENT_TOUCH_BEGAN )
-- Добавляем лисенер к слою
local eventDispatcher = gameLayer:getEventDispatcher()
eventDispatcher:addEventListenerWithSceneGraphPriority(listener, gameLayer)
--Добавляем слой к сцене
mainScene:addChild(gameLayer)
--Запускаем или если уже что-либо запущено заменяем сцену
if cc.Director:getInstance():getRunningScene() then
cc.Director:getInstance():replaceScene(mainScene)
else
cc.Director:getInstance():runWithScene(mainScene)
end
end
function resetVariables()
rotation = 0
scale = 1
ox = 0
oy = 0
delta = 20
moving = 0
end
--Запускаем сцену
init()
Теперь немного о помощи с поиском ответов. Во-первых: Порт на луа практически идентичен С++ и основные отличия можно прочесть здесь. Во-вторых: как я ранее писал, если вы не можете найти что-либо в порте С++, то попробуйте поискать эту проблему для JS движка. Он более популярен чем луа, однако в нем есть множество отличий, но очень часто он мне помогал. Чтобы запустить то, что у нас вышло нажмите F11. Чтобы запустить на андроид нажмите на кнопку «Debug configurations» и выберите андроид. Для запуска нужен настроенный adb, но про это и так много статей, поэтому я не буду писать здесь об этом. На этом все Спасибо за прочтение (:
Вот что вышло в итоге:
Автор: yegorf1