Привет всем подвальным дефолдерам и не только!
Эта тема может послужить вам отправной точкой при знакомстве с Монархом (Monarch) в Defold. Как минимум, с помощью этого пошагового руководства вы сможете создать контроллер, отвечающий за переходы между сценами вашей простой игры:

Я писал это руководство с той целью, чтобы новичок мог воспроизвести этот материал пошагово, некоторые моменты, связанные с Defold я не разбирал, предоставляя это для самостоятельной работы. Иногда я приводил простые аналогии, которые мне кажутся уместными.
Это руководство может подойти и для первого знакомства с GUI.
Исходный проект и “Пошаговое руководство” в READ.ME можете скачать здесь: defolder/forum/lessons/monarch/monarch_1 at main · dprogrb/defolder · GitHub
Готовый проект: defolder/forum/lessons/completed_project/cp_monarch_1 at main · dprogrb/defolder · GitHub
Монарх (Monarch) — это менеджер экранов (screen manager) для движка Defold, который упрощает навигацию между различными экранами игры (меню, уровни, настройки, попапы). Он работает на основе стека экранов : когда вы показываете новый экран, он добавляется на вершину стека, а при возврате назад верхний экран удаляется.
Установка Монарха
Вы можете использовать Monarch в своём проекте, добавив этот проект в качестве зависимости библиотеки Defold.
Как добавить библиотечную зависимость в проект на Defold
Ссылка на GitHub-репозиторий Monarch: GitHub - britzl/monarch: Monarch is a Defold screen manager with transition support and a straight forward API
В разделе “Installation” вы можете найти ссылки на все версии монарха:
После добавления Monarch в проект, у нас открывается новая способность в редакторе:
“Мы можем создавать несколько файлов одним кликом, и они будут находиться в одной коллекции”.
Создай папку “New Folder”:
Для примера, я назвал папку “test”.
Теперь кликни правой кнопкой мыши по папке test:
Выбери пункт “Create Monarch Scene From Directory”.
Теперь в директории test у нас несколько созданных файлов:

Если мы перейдём в
test.collection, то увидим, что в ней создан игровой объект monarch и в него добавлен компонент gui:
Если мы посмотрим на gui, то к gui автоматически был прикреплен gui script:
Если мы посмотрим содержимое gui скрипта, то увидим там код:
Мы можем создать один файл, а потом использовать вышеприведенный способ, чтобы получить тоже самое:

Попробуйте сами поэкспериментировать.
Нюансы
Если создать попробовать создать Монарх сцену, опираясь на созданный gui script, то изменений в gui script не будет:
Не добавляется строка с добавлением модуля монарха:
В этом скрипте редактора можно изменить шаблон создания файлов:
Как вы могли понять. Каждый экран в Monarch состоит из трёх компонентов:
Collection (.collection) — содержит игровые объекты экрана.
GUI (.gui) — визуальный интерфейс экрана.
GUI Script (.gui_script) — логика интерфейса.
Пошаговое руководство
Если вы не скачали файл с github и решили пройти урок в своём проекте, то исходная файловая структура такова:
Структура узла в
button.gui:План работ
Создать сцену для главного меню.
Создать сцену для игры.
Создать сцену для игрового меню.
Создать gui-макеты для каждой сцены.
Реализовать переключение между сценами, используя Монарх и GUI-узлы.
- Переход с сцены main_menu в game.
- Открытие game_pause.
- Переход с сцены game_pause в сцены main_menu и game.
Создадим для каждой сцены шаблон из файлов:
У вас должно получиться вот так:
Кликните по
game.collection и collection, задайте имя game:Для других коллекций сделайте аналогичные действия:
Создадим GUI макеты
Для начала перейдём в game.project и узнаем заданные настройки ширины и высоты экрана:
Теперь создадим корневой узел для макета в
main_menu.gui (в следующий раз я не буду упоминать это действие):Присвоим ему имя “root” и выставим такие значения:
Посмотрите, теперь наш корневой узел расположен по всему экрану:
Это необязательно, но можно ещё установить и Stretch в Adjust Mode:
Теперь в наш root поместим шаблон кнопки из папки
gui/templates/button.gui:Изменим ей id на button_start (для удобства):
Создадим в папке controller скрипт
controller.script:
Перейдём в
controller.collection и создадим там игровой объект и затем переименуем его на controller:Добавим компонент
controller.script:В этой же коллекции controller.collection создадим игровой объект с id main_menu:

В этом игровом объекте мы создадим компонент Collection Proxy (Прокси Коллекцию):
В качестве коллекции в этом компоненте укажем коллекцию
main_menu.collection:Переименуем её на
main_menu_proxy (для удобства):
Тем же способом, которым мы добавили в игровой объект компонент
controller.script, добавим скрипт монарха screen_proxy:В свойствах этого скрипта мы внесём изменения.
В параметр screen_proxy зададим имя нашей прокси-коллекции —
main_menu_proxy.В параметр screen_id зададим значения main_menu. С помощью этого имени мы будем обращаться к этой прокси при работе с API Монарха.
Перейдём в
controller.script и вставим такой код:
-- controller.script
local monarch = require "monarch.monarch" -- [1]
function init(self) -- [2]
msg.post(".", "acquire_input_focus") -- [3]
msg.post("#", "start_main_menu") [] -- [4]
end
function on_message(self, message_id, message, sender) -- [5]
if message_id == hash("start_main_menu") then -- [6]
monarch.show(hash("main_menu")) -- [7]
end
end
Сохраним проект (ctrl + S).
Соберем и запустим проект (ctrl + B):
Постараюсь объяснить по-простому:
- Делаем возможным использовать код, расположенный в этой папке:
- Функция, которая начинает своё выполнение кода, только в процессе создания игрового объекта.
- Отправляем сообщение игре: “Считывай данные с устройств ввода”. Как пример, с мыши или клавиатуры. Т.е если я кликаю на клавишу мыши или набираю что-то на клавиатуре, то компьютер это обрабатывает.
- Отправляем сообщение в этот же скрипт (для этого указана
"#", чтобы скрипт обработал его). - Функция для обработки сообщений, поступающих в этот скрипт (у нас это
controller.script). - Проверяем, если пришло сообщение с хэшем
hash("start_main_menu"). - Используем функцию из файла monarch.lua (модуля), чтобы показать сцену main_menu (мы указывали в screen_proxy screen_id):

Если привести аналогию с человеком.
Человек спит.
Во время пробуждения он говорит себе: “Я могу работать”.
А потом говорит себе: “Отрой глаза” и с помощью функции глаз он открывает глаза.
Если вы зайдёте в monarch.lua то увидите созданную там функцию show, которая позволяет открыть прокси-коллекцию:
Давайте внесём изменения в
main_menu.gui, изменим цвет корневому узлу:Теперь сохраним проект и снова его запустим:
Вы также можете изменить текст для текстового узла кнопки здесь:
Скрывать узлы с помощью свойства параметра:
Теперь создайте подобным образом другие сцены: game, game_pause:
Макет сцены game:
Макет
game_pause.gui:В
controller.collection создайте игровые объекты, содержащие прокси-коллекции и скрипт screen.proxy для остальных сцен:Перейдём в
main_menu.gui_script и вставим такой код:
-- main_menu.gui_script
local monarch = require "monarch.monarch"
function init(self)
self.button_start = gui.get_node("button_start/root") -- [1]
self.button_text = gui.get_node("button_start/text") -- [2]
-- Настраиваем текст кнопки
gui.set_text(self.button_text, "START") -- [3]
-- Запрашиваем input focus
msg.post(".", "acquire_input_focus")
end
function on_input(self, action_id, action)
if action_id == hash("touch") then
-- Проверяем клик по кнопке
if gui.pick_node(self.button_start, action.x, action.y) then -- [4]
if action.pressed then -- [5]
gui.set_scale(self.button_start, vmath.vector3(0.97, 0.97, 1)) -- [6]
elseif action.released then -- [7]
gui.animate(self.button_start, gui.PROP_SCALE,
vmath.vector3(1.03, 1.03, 1),
gui.EASING_INOUTQUAD,
0.3, 0,
function()
-- Вернем масштаб после анимации и переключим окно
gui.set_scale(self.button_start, vmath.vector3(1, 1, 1))
monarch.show(hash("game"))
end,
gui.PLAYBACK_ONCE_PINGPONG
) -- [8]
end
return true -- [9]
end
end
end
- Получаем id узла и записываем его в переменную
self.button_start. - Получаем id узла и записываем его в переменную
self.button_text. - Устанавливаем свойство text равное “START” текстовому узлу, ссылающемуся на значение, хранящиеся в переменной
self.button_text. - Если кликнули по узлу
button_start/root(значение хранится вself.button_start) был совершен"touch", то выполни определенные команды. - Если клавиша из game.input_binding
(touch)была нажата. - Измени масштаб коревому узлу кнопки.
- Если клавиша из input_binding
(touch)была отпущена. - Проиграй анимацию для кнопки.
- Прекращай обрабатывать этот actions(touch).
После клика на кнопку “START” вы переходите на коллекциюgame.collection:

Теперь перейдём в файлinput_bindingи добавим новую привязку ввода:

Вgame.gui_scriptдобавим такой код:
-- game.gui_script
local monarch = require "monarch.monarch"
function init(self)
msg.post(".", "acquire_input_focus")
end
function on_input(self, action_id, action)
if action_id == hash("esc") and action.pressed then -- [1]
monarch.show(hash("game_pause"), {no_stack = true}) -- [2]
end
end
- Если была нажат клавиша с привязкой на esc.
- Показываем сцену монарха, не помещая его в стек вызовов монарха.
Сохраним и запустим проект, нажмём на клавишу “ESC” на клавиатуре:

Теперь перейдём в game_pause.gui_script и вставим такой туда код:
local monarch = require "monarch.monarch"
function init(self)
-- Получаем узлы
self.button_continue_root = gui.get_node("button_continue/root") -- root кнопки
self.button_continue_text = gui.get_node("button_continue/text") -- text кнопки
self.button_to_menu_root = gui.get_node("button_to_menu/root") -- root кнопки
self.button_to_menu_text = gui.get_node("button_to_menu/text") -- text кнопки
-- Настраиваем текст кнопки
gui.set_text(self.button_continue_text, "CONTINUE")
gui.set_text(self.button_to_menu_text, "MENU")
-- Запрашиваем input focus
msg.post(".", "acquire_input_focus")
end
function on_input(self, action_id, action)
if action_id == hash("esc") and action.pressed then
monarch.hide(hash("game_pause")) -- [1]
end
if action_id == hash("touch") then
-- Проверяем клик по кнопке
if gui.pick_node(self.button_continue_root, action.x, action.y) then
--print("Button picked!")
if action.pressed then
gui.set_scale(self.button_continue_root, vmath.vector3(0.97, 0.97, 1))
elseif action.released then
gui.animate(self.button_continue_root, gui.PROP_SCALE,
vmath.vector3(1.03, 1.03, 1),
gui.EASING_INOUTQUAD, 0.3, 0,
nil, gui.PLAYBACK_ONCE_PINGPONG)
gui.set_scale(self.button_continue_root, vmath.vector3(1, 1, 1))
end
monarch.hide(hash("game_pause")) -- [1]
return true -- Потребляем input
end
if gui.pick_node(self.button_to_menu_root, action.x, action.y) then
if action.pressed then
gui.set_scale(self.button_to_menu_root, vmath.vector3(0.97, 0.97, 1))
elseif action.released then
gui.animate(self.button_to_menu_root, gui.PROP_SCALE,
vmath.vector3(1.03, 1.03, 1),
gui.EASING_INOUTQUAD, 0.3, 0,
nil, gui.PLAYBACK_ONCE_PINGPONG)
gui.set_scale(self.button_to_menu_root, vmath.vector3(1, 1, 1))
end
-- Закрываем game_pause
monarch.hide(hash("game_pause")) -- [1]
monarch.show(hash("main_menu"), { clear = true }) -- [2]
return true -- Потребляем input
end
end
end
monarch.hide(hash("game_pause"))закроет попап, показанный сno_stack.monarch.show(hash("main_menu"), { clear = true })удалит все экраны из стека и покажетmain_menu.
Сохраняем и запускаем проект:

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













































