Пишем свою первую игру на love
В прошлом посте я познакомил вас с движком love. Пришло время рассмотреть ближе движок, Lua (язык, который в нём используется) и написать свою первую игру.
Обмолвлюсь сразу, что у Love есть свой форум и вики, поэтому, если что можно почерпать информацию там.
Если вы программировали до love, вам будет проще понять его и Lua. Поэтому я попробую описать некоторые особенности в сравнении, например, с Game Maker.
Love содержит большое кол-во встроенных функций для работы со всеми аспектами разрабатываемых на нём проектов, но даже если вам их не хватает, вы можете их написать сами.
Как использовать love?
Чтобы начать писать игру, нужно создать в любом месте папку с любым названием и создать в нём файл main.lua, именно этот файл будет вызываться при запуске игры. Также в папку можно засунуть все нужные файлы для игры, включая другие скрипты Lua.
Чтобы запустить игру, перетяните папку на love.exe или его ярлык.
Чтобы запускать игру по двойному клику, как .exe, нужно запаковать содержимое папки (именно содержимое, а не саму папку) в архив и поменять расширение на .love.
Чтобы компилировать игру в .exe, можно использовать эту мини-программу, написанную YellowAfterlife. Просто перетяните файл .love на compile.bat и в папке в скором времени появится .exe.
Код игр на Lua состоит из функций, выглядит это так:
function love.load()
// действие
end
По сути, функции (для лучшего восприятия, что это) можно назвать аналогом событий в GM. В этом уроке мы разберём 3 главных функции, без которых нельзя обойтись и несколько собственных.
function love.load() - аналог Create, проигрывается всего один раз в начале игры
function love.update() - аналог Step, проигрывается постоянно
function love.draw() - аналог Draw, проигрывается каждый кадр
Стоит учесть основные особенности Lua:
1) Здесь нет { } как блоки кода, эти два символа работают при создании таблиц. Вместо них в условиях и функциях нужно использовать <ничего> и end.
2) Однострочные комментарии здесь ставятся так:
-- текст
Многострочные:
[[ текст 1
текст 2
текст 3]]
3) При создании условия здесь нельзя использовать =, обязательно ==. После условия должен стоять оператор then.
4) Объекты тоже создаются просто. Указывается переменная (название), ставится равно и в фигурных скобках записываются свойства. Есть несколько способов это записать:
4.1) hero = {x = 16, y = 16, speed = 10}
4.2)
hero = {}
hero.x = 16
hero.y = 16
hero.speed = 10
4.3)
hero = {
x = 16,
y = 16,
speed = 10
}
Лично я выбираю такой способ.
В принципе, теперь можно написать небольшую игру. Лично я пишу простенькую аркаду для изучения love. В ней мы собираем друзей и приводим их к выходу. Её мы и возьмём как пример.
Код, с которого нужно начинать написание игры выглядит так:
function love.load()
end
function love.update()
end
function love.draw()
end
А теперь дописываем dt как аргумент к love.update. Т.е. так: love.update(dt). dt - время, которое прошло с момента использования прошлого кадра.
Я рекомендую вам не читать между строк, а просматривать код, иначе ничего не поймёте.
А теперь создадим 3 объекта: hero (игрок), friend (друг), quit (выход) и зададим им свойства.
function love.load()
text = "Sandbox" -- текст для теста
love.graphics.setBackgroundColor(0, 128, 55) -- меняем цвет фона (R, G, B)
-- герой
hero = { -- герой
x = 16, -- x
y = 16, -- y
xstart = x, -- стартовый x
ystart = y, -- стартовый y
speed = 300, -- скорость
hp = 100, -- hp
w = 32, -- ширина
h = 32, -- высота
}
-- аналогично с другими
-- друг
friend = {
x = 320,
y = 240,
xstart = x,
ystart = y,
speed = 200,
hp = 100,
w = 16,
h = 16,
ai = 0 -- бежит, ли за игроком (начинает бежать, после того, как дотронется по него)
}
-- выход
quit = {
x = love.mouse.getX(), -- X равен x курсора
y = love.mouse.getY(), -- Y равен y курсора
xstart = x,
ystart = y,
w = 16,
h = 16,
}
end
Как видите, это совсем не сложно. Что дальше? Научим героя ходить.
Чтобы удобнее было работать, мы создадим для героя и других объектов функции движения и рисования.
К содержанию функции love.update(dt) допишем строку hero_movement(dt), я специально не создаю функции лично для каждого объекта, чтобы их могли использовать и другие объекты.
Теперь создадим саму функцию:
function hero_movement(dt)
-- вверх
if love.keyboard.isDown("up") or love.keyboard.isDown("w") then -- если нажата клавиша вверх или W
hero.y = hero.y - hero.speed * dt -- отнимаем от y скорость, умноженную на delta time
end
-- аналогично с остальными, я думаю, это должно быть понятно
-- вниз
if love.keyboard.isDown("down") or love.keyboard.isDown("s") then
hero.y = hero.y + hero.speed * dt
end
-- влево
if love.keyboard.isDown("left") or love.keyboard.isDown("a") then
hero.x = hero.x - hero.speed * dt
end
-- вправо
if love.keyboard.isDown("right") or love.keyboard.isDown("d") then
hero.x = hero.x + hero.speed * dt
end
Теперь герой может двигаться. Но это не имеет значения, если его не видно. Поэтому в функцию love.draw() добавим строку hero_draw().
Это значит, что нам нужно создать функцию для рисования. Вообще, можно, конечно, всё это делать в love.draw(), но это неудобно.
love.graphics.setColor(255, 255, 255, 255) -- определяем цвет для рисования (R, G, B, прозрачность)
love.graphics.rectangle("fill", hero.x, hero.y, hero.w, hero.h) -- рисуем прямоугольник на x, y героя с заданной в love.load() шириной и высотой и заполняем его "внутренности", используя режим "fill"
Вставляем в hero_draw() этот код. Теперь герой может ходить и его видно.
Теперь создадим друга. Прописываем friend_movement(dt) в love.update(dt), а friend_draw() в love.draw().
Теперь создадим функции.
Движение:
function friend_movement(dt)
-- герой взял к себе в компанию
if friend.ai == 1 then
-- влево
if friend.x > hero.x then -- если находится правее, чем герой
friend.x = friend.x - friend.speed * dt
else
-- вправо, если находится левее, чем герой
friend.x = friend.x + friend.speed * dt
end
-- вверх
if friend.y > hero.y then -- если находится ниже, чем герой
friend.y = friend.y - friend.speed * dt
else
-- вниз, если находится выше, чем герой
friend.y = friend.y + friend.speed * dt
end
end
end
Как получилось, что ai стал равным 1, расскажу чуточку позже, а пока мы напишем рисование спрайта для друга уже знакомыми вам функциями:
function friend_draw()
love.graphics.setColor(0, 112, 255, 255)
love.graphics.rectangle("fill", friend.x, friend.y, friend.w, friend.h)
love.graphics.setColor(255, 255, 255, 255)
love.graphics.print(friend.ai, 0, 10)
end
Теперь герой может ходить, друг следовать за героем, но как же всё-таки ai стало равным 1?
После соприкосновения героя с другом, значение ai меняется на 1. Как это случилось? Создадим функцию для просчёта коллизий (столкновений):
function CheckCollision(ax1, ay1, aw, ah, bx1, by1, bw, bh)
local ax2, ay2, bx2, by2 = ax1 + aw, ay1 + ah, bx1 + bw, by1 + bh
return ax1 < bx2 and ax2 > bx1 and ay1 < by2 and ay2 > by1
end
Теперь допишем это к hero_movement(dt):
if CheckCollision(hero.x, hero.y, hero.w, hero.h, friend.x, friend.y, friend.w, friend.h) then
friend.ai = 1
end
Функция CheckCollision содержит 8 аргументов и вот, что они означают:
hero.x = x первого объекта
hero.y = y первого объекта
hero.w = ширина спрайта первого объекта
hero.h = высота спрайта первого объекта
Аналогично с вторым объектом (friend).
Для окончания основной части разработки такой игры нужно создать выход. Для него будет только событие рисования.
function quit_draw()
love.graphics.setColor(0, 0, 0, 255)
love.graphics.rectangle("fill", quit.x, quit.y, quit.w, quit.h)
-- выход
if completed() then
love.graphics.print("You completed the level!", love.mouse.getX(), love.mouse.getY())
end
end
Выглядеть оно будет так. Если уровень выполнен (друг взят), то объект выхода будет рисовать на x, y курсора текст. Как узнать, пройден ли уровень? Создадим функцию completed():
function completed()
-- выход
return CheckCollision(hero.x, hero.y, hero.w, hero.h, quit.x, quit.y, quit.w, quit.h) and friend.ai and CheckCollision(friend.x, friend.y, friend.w, friend.h, quit.x, quit.y, quit.w, quit.h)
end
Она работает с помощью оператора return. Если всё правильно: друг стоит рядом с выходом, друг следует за ним и в данный момент стоит рядом с дверью, а не плетётся позади, то всё работает как нужно и return возвращает 1, сигнал, что всё правильно. Если что-то не так, то он возвращает 0.
Должно получиться что-то вроде этого:
Взглянуть на текущий код этой игры можно здесь.
Работает на 0.7.2
P.S. Наконец-то дописал эту статью и безумно рад, что день прошёл не зря. :)
- 10 февраля 2012, 22:51
- 020
Собственно, вопрос - откуда этот сигнал идёт?
Это нужно для того, чтобы при перемещении герой двигался с одинаковой скоростью на компьютерах любых мощностей.
Прошу прощения, по поводу dt немного ошибся: это время, которое прошло с момента прошлого кадра.
Скорее всего, не прошлого кадра, а именно прошлого апдейта. Не факт, что апдейт выполняется на каждой отрисовке кадра. Как правило, реже (раз в 10-30мс).
Скорее всего - не пойдёт. Нужны точные данные, чтобы написать правильно (хотя, в этой ситуации это не так уж и важно).
https://love2d.org/wiki/love.timer.getDelta
Ну тут фреймы
Дельта дельте рознь. В апдейт передается дельтаТайм между апдейтами, не факт, что оно равно дельте между фреймами.
мне кажется почти чуть более чем полностью очевидным что это время между апдейтами.
как юзер движка скажу что объекты там двигаются ровно, а кадры не больше апдейтов (всячески нагружал и ту и другую функцию)
Огромное спасибо за урок! Пиши еще. Как оказалось, это намного интереснее конструкторов))
Только не мог бы ты конкретней объяснить, что делает этот код?
ah, bx1, by1, bw, bh)
local ax2, ay2, bx2, by2 = ax1 + aw,
ay1 + ah, bx1 + bw, by1 + bh
return ax1 bx1
and ay1 by1 end
Согласен, имхо, писать на высокоуровневом языке гораздо приятнее, чем тыкать мышкой в конструкторе (:
Обычная проверка на столкновение двух прямоугольников.
на низкоуровневом еще приятнее)
Плюсы закончились :-(
А у меня еще есть=D
Ну, если мазохист, то пожалуйста :3
навык программирования определяется как раз по низкоуровневым языкам) на высокоуровневом и школьники легко напишут)
Навык программирования определяется совсем не этим (: Давайте обойдёмся без споров на тему вкусовщины
ок
Одному мне кажется процедурная парадигма легче в понимании, чем ООП?
Наверное :-)
Это ты ещё функциональную не видел.
Да кто спорит-то... Вопрос в эффективности, а не в лёгкости понимания. :)
Честно, уже не представляю как можно писать без объектов. Видимо, постепенно вырабатывается ООП головного мозга.
Прототипы тоже весьма неплохи.
Я попробую объяснить
Аргументы функции: ax1, ay1 — координаты первого объекта (а), от верхнего левого угла , aw, ah — его ширина и высота, аналогично для второго объекта (b).
Далее создаются и назначаются локальные переменные ax2, ay2 — координаты объекта "а" от нижнего правого угла (путем прибавления к начальным координатам высоты и ширины), таким образом, теперь можно определить объект как прямоугольник. Аналогично для второго объекта "b". Для краткости записи, все действия производятся в одну строку ("a, b = 2, 5" аналогично "a = 2, b=5").
Return, в данном случае, возвращает логический результат (true\false) сравнений всех этих координат, которые определяют, пересекаются ли объекты. Т.е. все аналогично операциям с "if". Думаю, основы логики с "and" расписывать нет нужды.
Всё, понял, спасибо :)
Рад помочь. Конечно, интереснее, ведь ты всё пишешь сам. И перед тобой бесконечные возможности. В GM тоскливо: такое ощущение, что всё написано за тебя. Заставить себя запустить Game Maker после love сродни самоубийству. :)
выгляд здорово. неплохо вариант для быстрого прототипирования.
вопрос: для запуска .exe файла на компьютере должно быть что-то предустановлено?
Нет. А вот для запуска .love файла, естественно, нужен сам love.
только надо вместе с .exe таскать библиотеки которые лежат в папке с love.exe
Начал читать - немного опечалили объяснения через Game Maker. Я им не пользовался, поэтому сразу теряюсь при упоминаниях Т-Т
Я не очень понял вот что - Как на Love делать объекты? Или каждую стенку надо вручную прописывать? Так, язык довольно интересный, но, имхо в том же GM справка куда понятнее написана, чем здесь вики. Очень неудобно искать что-то нужное. Т.е., например, если хочешь посмотреть что-то про частицы, нужно найти где-нибудь (в графике) упоминание про частицы, потом туда зайти и перейти в тему с частицами. Или можно как-то проще? можно, конечно в поиске ввести, но если не знаешь, как называется то, что ты ищешь, то вообще беда. Ощущение от программирования - дают тебе молоток и отвертку, и просят смастерить за недельку космический корабль. (непонятно с чего начинать)
всегда было всё понятно, если вы не знаете что ищите то возможно оно вам и не нужно, а так там есть оглавление где расписаны все модули.
да каждую стенку сам, мне это удобно, не надо больших/лишних нагромождений.
Объекты создаются теоретически также, как и в ГМ. С поправкой на использование метатаблиц. Написание функции, аналогичной instance_create() занимает менее минуты.
Не получается сделать .exe
Архивирую main.lua с расширением .love, перетаскиваю полученное на compile.bat. На полсекунды открывается консоль.
.exe не появляется. Что я делаю не так?
Подобная проблема уже была с моим другом. Но, к сожалению, не помню, как он разрешился. :(
Исправил некоторый код в уроке на более оптимальный и добавил в конце поста, что этот урок работает только в 0.7.2. Возможно, работает и в 0.7.* или 0.6.*.
И да, если кто-то хочет попробовать мой фреймворк на Lua, находящийся в неспешной разработке с апреля, вот он. Успехов. :)
Комментариев бы побольше, как им пользоваться)
Есть ли там мультиплеер?
LuaSocket подключен к Love2D.
Я про фреймворк.
Нет, мультиплеера нет.
Хочу продолжения...
http://prntscr.com/2on7ax
Другой часовой пояс, видимо.
Суровый челябинский Геймин
А что именно тебе хотелось бы?
Больше о столкновениях, организации выстрелов, построении уровней и переходы между ними, шейдеры... сейчас меня интересует любая информация, которая освещает создание игры с нуля...
Есть сайт love2d.ru, там есть статьи на интересную тебе тему.
гораздо интереснее когда разработчик игр сам пишет о своих решениях, проблемах и прочих "похождениях"
Кстати, разрабатывается порт движка - HÖVE.
Он нацелен на мобильные платформы, HTML5 и Flash.
Так что, уважаемые ловеры, скоро мы сможем писать игры и на мобилки.