Всем привет
Продолжаем серию разборов чужого кода с этой страницы: Публичный пример Defold .
Сегодня разбираем пример от britzl — Меню и игра.
Попробуйте этот проект в действии: запустите пример .
Исходная папка с проектом: ссылка на github .
Исходная папка с публичными примера Defold на github: ссылка на github.
Обращение к новичкам:
Я предполагаю, что у вас уже установлен Defold, если нет, перейдите по этой ссылке: Добро пожаловать в Defold.
Также, будет плюсом, если вы хотя бы поверхностно знакомы со строительными блоками Defold. Если нет, ознакомиться с основными концепциями Defold можно на официальном сайте — перейдите по этой ссылке.
Пример того, как скачать и открыть готовый проект:
Скачиваем архив с примерами проектов из github
Распаковываем скачанный ZIP архив примеров в любую папку во вашему усмотрению.
Переходим в папку examples
.
Ищем проект play_animation
.
Открываем game.project
.
Внимание: скриншоты представлены ниже, это пример скачивания и открытия проекта, в этом примере мы рассматриваем п
Preformatted text
роект с названием play_animation, потому название папок проекта будет отличаться.


Структура проекта:
После запуска проекта, мы видим кнопку "play"
, кликнув по которой мы переходим на уровень. На этом уровне имеются персонажи, они исчезают, если кликать по ним. Удалив последнего, можно перейти в меню всё с той же кнопкой"play"
.
В этом примере показано главное меню и игра, которые определены в отдельных коллекциях и загружаются и выгружаются с использованием прокси-серверов коллекций через скрипт контроллера.
Рассмотрим файловую структуру проекта:
controller.collection
— является основной коллекцией проекта и именно она загружается при старте игры.
Если у вас не видно фонового изображения, смените значение
Z position
для игрового объекта bg
, находящегося в этой коллекции, например, на -0.5
.gameproxy
и menuproxy
— являются прокси-коллекциями, которые будут загружаться скриптом controller.script
Прокси-коллекция — это ссылка на другую коллекцию, которая находится в отдельном
.collection
файле и подключается к основной коллекции через collectionproxy компонент. Прокси-коллекции используются для того, чтобы разделять сцены/уровни по коллекциям. Для оптимизация памяти — неактивные уровни не загружаются. Для упрощения переходов между экранами.
controller.script
— в этом скрипте происходит централизованное управление логикой игры. Этот скрипт отвечает за переключения между игровым меню и игровым уровнем.
В нашем случае, после того, как игрок нажмёт на "play"
. Будет осуществлён переход с "menyproxy"
на "gameproxy"
.
game.collection
— коллекция игрового уровня, которая будет загружаться после того, как в menu.collection игрок нажмёт на кнопку "play".
Содержит в себе игровой объект go
, который включает в себя компонент фабрики (прототип thing.go
) , и скрипт, отвечающий за логику уровня.
menu.collectrion
— коллекция, отвечающая за меню. Загружается при старте игры controller
скриптом. Имеет в себе игровой объект menu
, включающий в себя только gui-компонент
.
menu_and_game.atlas
— содержит изображения, которые будут использоваться в меню и в уровне.
thing.go
— прототип игрового объекта, фабрика при создании будет создавать игровой объект по этому шаблону.
menu.gui
— визуальная сцена интерфейса, содержащая в себе только один шаблон кнопки — "play"
.
button.gui
— кнопка "play"
. Созданная с помощью строительного компонента — node
. С типом узла — box
. И расположенного внутри text-нода
.
Рассмотрим controller.script:
controller.script
— управляющий скрипт, который загружает и переключает между двумя сценами через прокси-коллекции: меню (#menuproxy
) и игру (#gameproxy
).
msg.post(".", "acquire_input_focus")
msg.post("#menuproxy", "load")
msg.post(".", "acquire_input_focus")
— запрашиваем фокус ввода, чтобы скрипт мог обрабатывать события от клавиатуры, мыши, касания и т.д, в нашем примере, чтобы мы могли кликать.
msg.post("#menuproxy", "load")
— загружает коллекцию menu.collection c id menuproxy
msg.post(".", "release_input_focus")
Освобождает фокус ввода, когда объект будет уничтожен.
function on_message(self, message_id, message, sender)
— это обработчик сообщений, реагирует на события, приходящие в скрипт.
if message_id == hash("show_game") then
msg.post("#gameproxy", "load")
msg.post("#menuproxy", "unload")
Загружается игровая сцена (#gameproxy
).
Когда приходит сообщение show_game
загружает прокси-коллекцию gameproxy
. А та, уже в свою очередь, по id открываетgame.collection
.
Выгружается меню (#menuproxy
).
elseif message_id == hash("show_menu") then
msg.post("#menuproxy", "load")
msg.post("#gameproxy", "unload")
Аналогично, но наоборот: загружаем меню, выгружаем игру.
elseif message_id == hash("proxy_loaded") then
print("proxy_loaded", sender)
msg.post(sender, "enable")
После загрузки прокси приходит сообщение proxy_loaded
.
Выводим лог.
Активируем сцену командой enable.
Без enable коллекция не будет обновляться и отображаться.
elseif message_id == hash("proxy_unloaded") then
print("proxy_unloaded", sender)
Просто логируем, когда коллекция выгрузилась.
Как я полагаю, согласно информации с этой страницы: Компонент Collection Proxy.
Желательно, чтобы код выглядел вот так:
function on_message(self, message_id, message, sender)
if message_id == hash("show_game") then
msg.post("#gameproxy", "load")
msg.post("#menuproxy", "final")
msg.post("#menuproxy", "disable")
msg.post("#menuproxy", "unload")
elseif message_id == hash("show_menu") then
msg.post("#menuproxy", "load")
msg.post("#gameproxy", "final")
msg.post("#gameproxy", "disable")
msg.post("#gameproxy", "unload")
elseif message_id == hash("proxy_loaded") then
print("proxy_loaded", sender)
msg.post(sender, "init")
msg.post(sender, "enable")
elseif message_id == hash("proxy_unloaded") then
print("proxy_unloaded", sender)
end
end
Рассмотрим game.script:
game.script
— реализует простую игру, где нужно кликнуть по всем инопланетянам, чтобы завершить игру.
math.randomseed(os.time())
math.randomseed(os.time())
делает math.random()
непредсказуемым.
В Lua, как и в большинстве языков, генератор случайных чисел math.random() по умолчанию использует одинаковое начальное значение (seed), что делает случайные числа повторяемыми при каждом запуске игры. Чтобы избежать этого и получить разные значения каждый раз, нужно задать seed случайно, и чаще всего используется текущее время:
self.things = {}
for i = 1, 5 do
local id = factory.create("#factory", vmath.vector3(math.random(40, 996), math.random(40, 600), 0))
table.insert(self.things, id)
end
Создаём таблицу, которая будет хранить пришельцев.
В цикле создаём 5 игровых объектов через factory.create()
в случайных позициях.
Каждый объект сохраняется в self.things
.
function update(self, dt)
msg.post("@render:", "draw_text", { text = "Click and remove all the aliens to return to the menu", position = vmath.vector3(20, 30, 0) } )
end
Каждый кадр отрисовываем через render
текст-подсказку на экране.
function on_input(self, action_id, action)
if action.released then
for i=1,#self.things do
local id = self.things[i]
local pos = go.get_position(id)
if action.x > (pos.x - 30) and action.x < (pos.x + 30) and action.y > (pos.y - 35) and action.y < (pos.y + 35) then
go.delete(id)
table.remove(self.things, i)
break
end
end
if #self.things == 0 then
msg.post("controller:/controller", "show_menu")
end
end
end
Если клавиша отпущена, то заходим в цикл и проходим по таблице с пришельцами.
Получаем id и позицию n-пришельца.
Проверяем, если клик попал в прямоугольную область вокруг объекта (ширина: 60, длина: 70), то удаляем игровой объект. Выходим из цикла.
Если все объекты были уничтожены, размер таблицы равен 0, то отправляем сообщение контроллеру о том, что нужно показать menu.collection
.
Рассмотрим menu.gui_script:
Обратите внимание, menu.gui_script
— это GUI-скрипт, управляющий кнопкой "play"
в меню.
if action.pressed and gui.pick_node(gui.get_node("play/bg"), action.x, action.y) then
msg.post("controller:/controller", "show_game")
end
Проверяем, произошло ли нажатие: action.pressed
и точка касания находится внутри ноды: play/bg
. Если попадание по ноде есть, отправляем сообщение контроллеру: “запусти игру”.
Надеюсь, кому-нибудь этот материал будет полезен.
Также я буду рад получить обратную связь, чтобы в дальних постах я смог улучшить качество изложений/объяснений.
Всем спасибо за внимание
Другие разборы:
- Разбор проектов на Defold 1.Tilemap Collisions [tilemap]
- Разбор проектов на Defold 2. Параллакс [parallax]
- Разбор проектов на Defold 3. Движение игровых объектов [animation, movement, input]
- Разбор проектов на Defold 4. Воспроизвести анимацию [animation, movement, input]
- Разбор проектов на Defold 5. Меню и игра. Прокси-коллекции [proxy-collection, gameloop, collection, gui]