Главная » Хабрахабр » Флаппи Бёрд: — Поехали

Флаппи Бёрд: — Поехали

Это рассказ о том, как написать свою игру на Corona.
Уровень вхождения — минимальный (и ботаник с кафедры алгебры поймет).
Я напомню, что Corona — это движок для создания 2D игр на все платформы и, touch-touch, сегодня День космонавтики. Сюжет для игры выбран соответсвующий и, разумеется,
мы повторяем за первым космонавтом

-Поехали!

Что получится за 2 часа программирования?

Вот что получилось у меня за 2 часа программирования.
Разумеется, на всю эту ботву у вас уйдет в 3 раза меньше времени, поскольку я программист линейный, как алгебра.

[embedded content]

Узнали? Да, это игра адфззн ишкв из Вьетнама.
Напомню, что приложение приносило автору $50.000 в день. По-моему, неплохо.
Если присмотреться в моем клипе к осколкам столкновения птички со столбом, то видно надпись ХАБР. Эту картинку я использую в качестве текстуры для particle effect.

Установка инструмента Corona

Занимает 10 минут и прекрасно расписана во многих местах

Создание проекта gagarinbird

Запустите Corona на своей машине (у меня Mac) и выберите пункт меню Создать новый пустой проект.
В результате в указанном месте создается директория gagarinbird, в которой присутствует куча всевозможного полезного мусора, среди которого в дальнейшем мы будем редактировать один-единственный файл под названием

main.lua

Текстовый редактор — на ваше усмотрение. Язык программирования — Lua.

Прежде всего, нам понадобятся картинки и звуки для игры.

Как их заполучить, не мучая автора?

  • Скачиваем файл Flappy Bird [Dong Nguyen] (v1.2 LP os60)-Orbicos-ICPDA.rc309.ipa
  • Переименовываем в файл Flappy Bird [Dong Nguyen] (v1.2 LP os60)-Orbicos-ICPDA.rc309.zip
  • Разпаковываем файл Flappy Bird [Dong Nguyen] (v1.2 LP os60)-Orbicos-ICPDA.rc309.zip
  • Кликаем правой кнопкой мышки на файле Flap.app и выбираем пункт Show Package Content
  • Копируем картинки и звуки в нашу директорию gagarinbird

Для удобства сделайте поддиректорию gagarinbird/Assets и поместите сюда все картинки.
Аналогично делаем поддиректорию gagarinbird/Sounds и помещаем сюда все звуки.

Первые 2 строчки кода — проверка звуков Му

Вставьте в файл main.lua первые две строчки кода

dieSound = audio.loadSound( "Sounds/sfx_die.caf" )
audio.play( dieSound )

Нажмите в Corona (его многие называют Corona Simulator) 2 горячие кнопки /cmd/+R и наше приложение запустится в окне, похожем на телефон и издает звук смерти! Ай-ай, все работает.
Фон — девственно черный, но это не беда — мы только что научились играть любые звуки. Причем звучат они одинаково, что на Android, что на iPhone, что на (прости меня, Господи) Windows.

Загружаем звуки и оформляем все красиво

Делаем функцию loadSounds() и вызываем её

local function loadSounds() dieSound = audio.loadSound( "Sounds/sfx_die.caf" ) hitSound = audio.loadSound( "Sounds/sfx_hit.caf" ) pointSound = audio.loadSound( "Sounds/sfx_point.aif" ) swooshingSound = audio.loadSound( "Sounds/sfx_swooshing.caf" ) wingSound = audio.loadSound( "Sounds/sfx_wing.caf" ) boomSound = audio.loadSound( "Sounds/sfx_boom.mp3" )
end -- Start application point
loadSounds()

Загружаем картинку фона и обрабатываем tap-tap

Учимся рисовать картинку фона на весь экран телефона

 local function initBackGround() local ground = display.newImageRect( "Assets/ground.png", display.actualContentWidth, display.actualContentHeight ) ground.x = display.contentCenterX ground.y = display.contentCenterY
end -- Вызываем свежеиспеченную функцию
initBackGround()

Запустите (ctrl+R), получите примерно такую красивую картинку

Ура, теперь мы можем нарисовать любую картинку, разместить её в любом месте и трансформировать как хотим.
Например,

ground.rotation = 90

повернет картинку фона на 90 градусов.

Добавляем тыкание в экран.

 ground:addEventListener("tap", wing) local function wing() audio.play( wingSound ) end

Мы добавили Listener, который ловит все нажатия на ground и вызывает при этом функцию wing().
Если посмотреть код, каждое тыкание в наш ground должно вызывать звук wing.caf.
Запускаем — все работает.

Физика полета и таймер

В Corona есть библиотека physics, с гравитацией и столкновениями. Но для нашей игры она избыточна. Добавление объектов (птицы, Земли и столбов) и настройка параметров потребует больше кода, чем простой запуск таймера (40 раз в секунду) с проверкой столкновений и динамики движения в гравитационном поле Земли. Земля в иллюминаторе. Пардон, отвлекся.

Сделаем динамику на языке машины времени.

-- запускаем таймер, который вызывается каждые 25 миллисекунд
gameLoopTimer = timer.performWithDelay( 25, gameLoop, 0 ) local function gameLoop() vBird = vBird + dt * g yBird = yBird + dt * vBird end 

40 раз в секунду вызывается наша функция gameLoop()
Динамика движения птицы записана в двух строках этой функции. Здесь g — ускорение свободного падения (для iPhone равна 800 пикселей в секунду за секунду)

dt = 0.025 — шаг по времени

uBird — скорость птицы по оси X
vBird — скорость птицы по оси Y

xBird — координата птицы по оси X
yBird — координата птицы по оси Y

Динамика движения столбов и поверхности еще проще — движение происходит лишь по оси X

 for i=1,3 do pipes[i].x = pipes[i].x + dt * uBird end 

Состояния игры

Все кирпичи готовы, осталось все соединить в изящной быдло-программе
Итак, в нашей игре 4 состояния.

Состояние 0 (gameStatus=0) все замерло и готово к старту игры. Ждем нажатия на экран. При нажатии переходим в состояние 1.
Состояние 1 (gameStatus=1) птица летит, столбы движутся, гравитация работает. Нажатия на экран добавляют птице скорости строго вверх (vBird = jumpSpeed).
Состояние 2 (gameStatus=2) птица столкнулась с зеленым столбом и падает строго вниз, столбы стоят, гравитация работает. Нажатия на экран не влияет ни на что.
Состояние 3 (gameStatus=3) птица столкнулась с Землей, все замерло, подсчет очков и показ результата полета. Ожидаем нажатия для перехода в состояния 0.

В принципе, состояние 1 и 2 можно объединить, назначив в последнем горизонтальную скорость в 0, это дело вкуса.

Взмахи крыльями и спрайт-анимация

Наклон птицы пропорционален вектору скорости.
То есть равен арктангенсу угла.

 bird.rotation = math.atan(vBird/uBird)

Для анимации взмахов крыльями птицы при нажатии на экран используется спрайт-лист Corona. Это чуть сложнее, чем просто png картинка.


Сформируем кадры анимации в отдельный png файл. Размером 400 на 100 пикселей. Размер каждого внутреннего спрайта 100 на 100. То есть имеем 4 кадра.

Код инициализации птички следующий

local function setupBird() local options = { width = 100, height = 100, numFrames = 4, sheetContentWidth = 400, -- width of original 1x size of entire sheet sheetContentHeight = 100 -- height of original 1x size of entire sheet } local imageSheet = graphics.newImageSheet( "Assets/bird.png", options ) local sequenceData = { name="walking", start=1, count=3, time=300, loopCount = 2, -- Optional ; default is 0 (loop indefinitely) loopDirection = "forward" -- Optional ; values include "forward" or "bounce" } bird = display.newSprite( imageSheet, sequenceData ) bird.x = xBird bird.y = yBird
end

Теперь, в момент нажатия на экран вызываем строчку кода

bird:play()

И птичка машет крыльями 2 раза подряд.

Столкновение и particle effect

Проверка столкновения элементарна.
Во-первых, проверка столкновения с поверхностью Земли

 if yBird>yLand then yBird = yLand crash() end

Во-вторых, проверка столкновения со столбами

 local function checkCollision(i) local dx = 40 -- взято из размеров картинки столба local dy = 50 -- взято из размеров картинки столба local boom = 0 local x = pipes[i].x local y = pipes[i].y if xBird > (x-dx) and xBird < (x+dx) then if yBird > (y+dy) or yBird < (y-dy) then boom = 1 end end return boom
end

Добавим particle effect в месте столкновения птицы со столбом.
Код выглядит громоздко, но меняя 20 параметров эффекта, можно получить удивительные взрывы, всполохи и огненные шары.

local function setupExplosion() local dx = 31 local p = "Assets/habra.png" local emitterParams = { startParticleSizeVariance = dx/2, startColorAlpha = 0.61, startColorGreen = 0.3031555, startColorRed = 0.08373094, yCoordFlipped = 0, blendFuncSource = 770, blendFuncDestination = 1, rotatePerSecondVariance = 153.95, particleLifespan = 0.7237, tangentialAcceleration = -144.74, startParticleSize = dx, textureFileName = p, startColorVarianceAlpha = 1, maxParticles = 128, finishParticleSize = dx/3, duration = 0.75, finishColorRed = 0.078, finishColorAlpha = 0.75, finishColorBlue = 0.3699196, finishColorGreen = 0.5443883, maxRadiusVariance = 172.63, finishParticleSizeVariance = dx/2, gravityy = 220.0, speedVariance = 258.79, tangentialAccelVariance = -92.11, angleVariance = -300.0, angle = -900.11 } emitter = display.newEmitter(emitterParams ) emitter:stop() end

Оставляю этот кусок кода без комментария — просто побалуйтесь в параметрами сами.

local function explosion() emitter.x = bird.x emitter.y = bird.y emitter:start()
end

Function explosion() вызываем в момент столкновения птички со столбом. Я не смог пикселизовать эффект в стиле всех картинок игры. Возможно у вас есть совет, как это сделать. Если что, то Scale не сработал.

Спасибо

Весь проект бесплатно без смс можно будет скачать с Corona Marketplace во время майских праздников. Код проходит модерацию.

Кроме прочего, я стал евангелистом Corona, получил первую зарплату и все это благодаря публикации на Хабре. Спасибо ресурсу и конечно вам, читатели. Алгебраистам тоже привет.


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Современная веб-разработка: выбери себе приключение

Недавно я делал доклад о том, какие шишки можно набить, занимаясь современной веб-разработкой. Привет. Предлагаю вам почитать этот пост, выбрать свои варианты, дополнить квест и обсудить то, что наболело. Получилось что-то вроде квеста с распределенным выбором: от того, какой язык ...

Oh, My Code: Как стать успешным в IT

Что нужно, чтобы добиться успеха в IT? Об этом и многом другом мы поговорили в новом выпуске ток-шоу Oh, My Code с Дмитрием Гришиным, сооснователем и председателем совета директоров Mail.ru Group, основателем Grishin Robotics. Не могу не начать наш разговор ...