Pull to refresh

Знакомство с Marmalade Quick, новый игрок в быстрой кроссплатформенной разработке

Development of mobile applications *Game development *
Sandbox


Добрый день!

Честно говоря, я немного удивлен, почему на хабре об этом ещё нет упоминания. Возможно из-за того, что инди разработчикам уже ничего не светит? Быть может причина в запредельной цене, которую просит Marmalade за свой продукт? Или альтернатив столько много, что за всем и не уследить? Как бы там не было, сегодня поговорим именно об Marmalade Quick и постараюсь Вам показать, что свое название оно вполне оправдывает. Опыта в мобильных разработках я не имел, но Quick настолько прост, что за пару часов мне удалось написать полноценное приложение.

Почему бы и Вам не попробовать ...

Что собственно из себя представляет Marmalade Quick?


Marmalade Quick позволяет писать приложения на языке Lua, который сейчас имеет достаточно большую популярность среди разработчиков игр. Lua заманчив тем, он очень простой и достаточно мощный язык. Возможность подключения таких модулей как: Cocos2D-x для 2D графики, Box2D для физики и SQLite для локального хранения данных, JSON для обмена данными.

На данный момент версия 1.0, но уже реализовано и добавлено в API:
  • Воспроизведение аудио и видео;
  • Проведение платежных операция в Google Play, BlackBerry World, Apple App Store;
  • Компас и определение местоположения;
  • Авторизация в Facebook и обновление статуса;
  • Получение общей информации об устройстве;
  • Нативные UI компоненты;
  • Сокеты;
  • Физика;

Ядром Marmalade Quick является "OpenQuick", открытый и полностью независимый от Marmalade код.
Marmalade Quick и OpenQuick распространяется под лицензией MIT.
Задавать вопросы, на которые достаточно быстро дают решения, можно здесь.
API на английском языке здесь.

Подготовительная часть


  1. Скачиваем и устанавливаем Marmalade SDK 6.2 и выше (30 дней trial версия);
  2. Скачиваем Marmalade Quick. Распаковываем в папку с Marmalade SDK (/quick);
  3. Запускаем quickLaunchPad.exe в папке /quick/tools;
  4. Import Project — импорт готовых проектов, в частности можно протестировать работу «Hello World» из папки /quick/data/examples;
  5. New Project — создаем новый проект;

Переходим к разработке


Что у нас должно в конце получиться? — Бутылочка! Выбираем число игроков, крутим бутылку и на кого попадает горлышка, тот выполняет задание.



Создав новый проект, у вас создается папка с файлами проекта. Её расположение можно увидеть в Source Location, откроем её Open Folder. Папка в которой все наши файлы это /resources, а главный исполняющий файл main.lua, в нем мы и будем работать.

Определяем разрешение экрана:
local dx=director.displayWidth
local dy=director.displayHeight

Вычисляем число, на которое будем масштабировать все элементы на экране, считая что наше приложение рассчитано на экран не больше 1280x768:
local skalex = dx/1280
local skaley = dy/768

Создадим первую сцену, где расставлены бутылки для выбора числа игроков (от 4-х до 10-и):
local scene1 = director:createScene()

-- Рисуем фон
local fon = director:createSprite( {
    x=0, y=0, -- отсчет идет с левого нижнего угла
    source="fon.jpg", -- файл изображения
    xScale=skalex, yScale=skaley -- масштабирование, для экранов отличных от 1280x768
} )

-- По порядку расставляем бутылки
local player4 = director:createSprite( {
    x=dx/2-360*skalex, y=dy/2,
    xAnchor=0.5, yAnchor=0.5, -- точка вращения картинки, в нашем случае это её центр; 1 - правый верхний угол; 0 - левый нижний угол
    source="but/player4.png",
    xScale=skalex, yScale=skaley
} )

local player5 = director:createSprite( {
    x=dx/2-240*skalex, y=dy/2,
    xAnchor=0.5, yAnchor=0.5,
    source="but/player5.png",
    xScale=skalex, yScale=skaley
} )

local player6 = director:createSprite( {
    x=dx/2-120*skalex, y=dy/2,
    xAnchor=0.5, yAnchor=0.5,
    source="but/player6.png",
    xScale=skalex, yScale=skaley
} )

local player7 = director:createSprite( {
    x=dx/2, y=dy/2,
    xAnchor=0.5, yAnchor=0.5,
    source="but/player7.png",
    xScale=skalex, yScale=skaley
} )

local player8 = director:createSprite( {
    x=dx/2+120*skalex, y=dy/2,
    xAnchor=0.5, yAnchor=0.5,
    source="but/player8.png",
    xScale=skalex, yScale=skaley
} )

local player9 = director:createSprite( {
    x=dx/2+240*skalex, y=dy/2,
    xAnchor=0.5, yAnchor=0.5,
    source="but/player9.png",
    xScale=skalex, yScale=skaley
} )

local player10 = director:createSprite( {
    x=dx/2+360*skalex, y=dy/2,
    xAnchor=0.5, yAnchor=0.5,
    source="but/player10.png",
    xScale=skalex, yScale=skaley
} )

Так, у нас уже есть что-то вроде такого:



Теперь создадим сцену вторую, в ней то и происходит вся игра. Выводить поэтапно я не буду, а лучше напишу подробные комментарии.
local scene2 = director:createScene()

local fon = director:createSprite( {
    x=0, y=0,
    source="fon.jpg",
    xScale=skalex, yScale=skaley
} )

-- кнопка меню
local menu = director:createSprite( {
    x=dx, y=dy,
    xAnchor=1, yAnchor=1,
    source="menu.png",
    xScale=skalex, yScale=skaley
} )

-- кнопка "Раскрутить" бутылку
local buty = director:createSprite( {
    x=dx/2, y=20,
    xAnchor=0.5, yAnchor=0,
    source="raskrutit.png",
    xScale=skalex, yScale=skaley
} )

local neLez = nil -- создадим пустую переменную

local but = function()
    neLez = 2
    tween:to(buty, { alpha=1, time=0.5, onComplete = kruti} ) -- здесь происходит плавное появление за 0.5 сек выше созданной кнопки "Раскрутить" (kruti).
end

local win_p = "WIN" -- переменная, в которую заносится победитель

local fonz = function(event)
    audio:stopStream() -- остановим воспроизведение музыки
    r = math.random(1,42) -- создаем случайное число от 1 до 42
    s = "zadanie/"..r..".png" -- в папке 42 картинки с заданиями, случайным образом достаем одну из них
    -- выводим на экран задание
    local zadanie = director:createSprite( {
        x=dx/2, y=dy/2, -- центр экрана
        xAnchor=0.5, yAnchor=0.5,
        source=s,
        xScale=skalex, yScale=skaley
    } )
    -- выводим на экран кнопку "Сделано"
    local sdelano = director:createSprite( {
        x=dx/2, y=dy/2-180*skaley,
        xAnchor=0.5, yAnchor=0.5,
        source="sdelano.png",
        xScale=skalex, yScale=skaley
    } )
    -- выведем текст, с номером игрока  на котором остановилось горлышко
    local label = director:createLabel( {
        x=dx/2-90, y=dy/2+120,
        xAnchor=0.5, yAnchor=0.5,
        font="fonts/ComicSans24.fnt", -- файл шрифтов, обязательно формат .fnt
        color={99, 61, 2}, -- цвет
        xScale=3, yScale=3, -- увеличиваем размер шрифта на 3
        text="Player "..win_p
    } )
    local touchz = function(event)
        if event.phase == "began" then -- произошло событие нажатия - began; ended - завершение нажатия; moved - скольжение по экрану; 
            tween:to(label, { alpha=0, time=0.5} ) -- исчезает текст
            tween:to(zadanie, { alpha=0, time=0.5} ) -- исчезает задание
            tween:to(sdelano, { alpha=0, time=0.5, onComplete = but} )  -- исчезает кнопка "Сделано" и после исчезновения вызывается функция but, которая выводи на экран кнопку "Раскрутить"
        end
    end
    sdelano:addEventListener("touch", touchz) -- отслеживаем нажатие ("touch") на кнопку "Сделано" и после чего вызываем функцию touchz 
end

local kruti = function(event)
    if event.phase == "ended" then -- нажатие
        if neLez == nil or neLez == 2 then
            local payer = math.random(1,360) -- определяем, на кого попадёт горлышко, случайный градус от 1 до 360
            win_p = math.ceil(payer/(360/players)) -- вычисляем, на кого попало горлышко, зная сколько всего игроков играет
            -- бутылка, которой играют
            local butyl = director:createSprite( {
                x=dx/2, y=dy/2,
                xAnchor=0.5, yAnchor=0.5,
                source=play_but,
                xScale=skalex, yScale=skaley
            } )
            audio:playStreamWithLoop("mus.mp3", false) -- воспроизведение .mp3 файла, начинается при раскрутке бутылки, false - без повторений
            tween:to(buty, { alpha=0, time=0.5} ) -- исчезает кнопка "Раскрутить"
            tween:to(butyl, { rotation=payer+5400, easing=ease.expOut, easingValue=2, time=12} ) -- rotation - угол на который повернуть бутылку, в нашем случае мы его случайным образом создали, но чтобы кручение было побольше прибавим ещё 15 оборотов (5400 градусов), easing=ease.expOut - постепенное затухание за 12 сек
            tween:to(butyl, { alpha=0.1, delay=14, time=1, onComplete = fonz } ) -- delay - ждём 14 сек, делаем видимость бутылки по альфа каналу 0.1 и вызываем функцию fonz, которая отвечает за вывод задания на экран
            neLez = 1
        end
    end
end




Дальше собственно элементы управления для первой сцены, так как мы нарисовали только бутылки, а теперь добавим им нажатия.
local touch4 = function(event) -- функция нажатия на 4-ю бутылку
    players = 4 -- изменяем переменную, отвечающую за числом игроков
    play_but = "but/4player.png" -- файл бутылки, которой будем играть
    if event.phase == "began" then -- произошло нажатие
        director:moveToScene(scene2, {transitionType="crossFade", transitionTime=0.5}) -- переход к сцене 2
    end
end
local touch5 = function(event)
    players = 5
    play_but = "but/5player.png"
    if event.phase == "began" then
        director:moveToScene(scene2, {transitionType="crossFade", transitionTime=0.5})
    end
end
local touch6 = function(event)
    players = 6
    play_but = "but/6player.png"
    if event.phase == "began" then
        director:moveToScene(scene2, {transitionType="crossFade", transitionTime=0.5})
    end
end
local touch7 = function(event)
    players = 7
    play_but = "but/7player.png"
    if event.phase == "began" then
        director:moveToScene(scene2, {transitionType="crossFade", transitionTime=0.5})
    end
end
local touch8 = function(event)
    players = 8
    play_but = "but/8player.png"
    if event.phase == "began" then
        director:moveToScene(scene2, {transitionType="crossFade", transitionTime=0.5})
    end
end
local touch9 = function(event)
    players = 9
    play_but = "but/9player.png"
    if event.phase == "began" then
        director:moveToScene(scene2, {transitionType="crossFade", transitionTime=0.5})
    end
end
local touch10 = function(event)
    players = 10
    play_but = "but/10player.png"
    if event.phase == "began" then
        director:moveToScene(scene2, {transitionType="crossFade", transitionTime=0.5})
    end
end

local menu_back = function(event) -- при нажатии на кнопку меню
    if event.phase == "began" then
        audio:stopStream()
        director:moveToScene(scene1, {transitionType="crossFade", transitionTime=0.5}) -- переходим на первую сцену
    end
end

player4:addEventListener("touch", touch4) -- отслеживаем нажатие на бутылку 4
player5:addEventListener("touch", touch5) -- отслеживаем нажатие на бутылку 5
player6:addEventListener("touch", touch6) -- отслеживаем нажатие на бутылку 6
player7:addEventListener("touch", touch7) -- отслеживаем нажатие на бутылку 7
player8:addEventListener("touch", touch8) -- отслеживаем нажатие на бутылку 8
player9:addEventListener("touch", touch9) -- отслеживаем нажатие на бутылку 9
player10:addEventListener("touch", touch10) -- отслеживаем нажатие на бутылку 10

buty:addEventListener("touch", kruti) -- отслеживаем нажатие на кнопку "Ракрутить"
menu:addEventListener("touch", menu_back) -- отслеживаем нажатие на кнопку "Меню"

director:moveToScene(scene1) -- говорим, чтобы при запуске приложения главная была первая сцена, так как вначале сверху вниз делается обход всего кода и если не назначить сцену, то отобразится вторая, так как она находится ниже.


Вот собственно и все. Бутылка крутится, музыка играет… люди выполняют…

Ещё что хотелось добавить, так это то, что при запуске приложения на устройстве, вначале некоторое время черный экран, чтобы ускорить его исчезание сделайте следующее:
  • Создайте файл config.lua
  • Добавьте в него строку — config={debug={general = false}}
  • В файле app.icf добавьте в конец строку — configFileName=«config.lua»

Вот, теперь точно всё, успехов в разработке!
Tags:
Hubs:
Total votes 37: ↑32 and ↓5 +27
Views 7.9K
Comments Comments 26