How to: Делаем свой Disco Elysium в Construct 3

Играя в Disco Elysium помимо истории меня очень впечатлило визуальное исполнение. При всей технической простоте игры она выдает очень приятную картинку. Как им удалось сделать такую большую локацию набережной с уникальной детализацией без экранов загрузки? Вместо повторяющихся домиков с расставленными по разному ящиками, каждое здание выглядит по своему и имеет уникальные отпечатки времени в виде разрушенных стен, граффити или огромной мозаики на брусчатке.

How to: Делаем свой Disco Elysium в Construct 3

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

Я: Мам, можно Disco Elysium 2?
Мама: У нас уже есть Disco Elysium 2 дома.
Disco Elysium 2 дома:

Время идет, у самой ZA/UM начались сложности, ставящие продолжение Диско под большой знак вопроса. А остальные нераскрытые таланты без своей игры так и сидят без доступных инструментов. В этой статье я хочу поделиться методами, позволяющими воспроизвести такие огромные локации, как в Disco Elysium, но в рамках двухмерного движка Construct 3. Решение показалось не самым очевидным, поэтому хочу поделиться с аудиторией, вместе с несколькими другими полезными эффектами по теме. Полноценную гениальную игру не обещаю: для этого не хватает выдающегося художника, творческого гения саботирующего разработку продолжительными запоями и инвестора согласного спонсировать потенциальный шедевр. Если это вы, пожалуйста напишите мне.

Двухмерная графика обманчиво считается чем то простым и не требовательным. Вы может быть удивитесь, но 3D в игры пришло в том числе и для экономии места: покадрово анимированный персонаж во всех вариациях и направлениях движения займет больше места, чем его модель, текстуры и набор анимаций вместе взятые. Не каждый движок сейчас имеет бесконечный лимит полигонов в виде Люменов и не каждый игрок имеет аппарат способный потянуть эти Люмены. Поэтому у 2D игр вполне есть причины тормозить от нагрузок, а значит об оптимизации стоит задуматься у самого фундамента разработки. Чем раньше спохватишься, тем меньше неприятных сюрпризов это принесет в дальнейшем.

Construct 3 настоятельно советует не использовать большие изображения по нескольким причинам:

  • при старте сцены грузятся все объекты этой сцены
  • спрайты в памяти хранятся в несжатом виде и занимают еще больше места, чем упакованные
  • во время экспорта, движок собирает разные спрайты в общие квадратные атласы и большой кусок (например прямоугольный 1920х1080) может скомпоноваться не оптимально, оставляя много пустого места висящего в памяти бесполезным грузом
  • у игрока со слабым железом в итоге может просто не хватить видео памяти на вашу игру

В качестве решения, для сборки уровней предлагается использовать небольшие спрайты отдельных объектов.

Пример из руководства с Rayman Origins. Выделены повторяющиеся спрайты из которых собран весь уровень.
Пример из руководства с Rayman Origins. Выделены повторяющиеся спрайты из которых собран весь уровень.

Это безусловно хороший подход, дающий определенную выгоду (и мы его тоже используем в будущем). Но в примере DE нас интересуют уникальные изображения большого размера, расставляя которые описанным способом мы быстро упремся в лимиты памяти. Что тогда делать?

Подгрузка спрайтов

Идея заключается в следующем: мы разрежем огромную картинку фона на равные куски, которые будем подгружать в зависимости от положения персонажа в сцене.

Для демонстрационных целей пропущу творческий момент создания графики (у вас тут может быть что угодно: 3D рендер, диджитал арт, картина маслом или фотоколлаж). Я же сгенерирую окружение нейронкой и заапскейлю его до разрешения 15184х11792 пикселей, от которых зависает даже редактор.

С большого расстояния смотрится сносно. Увы, при необходимом для игры приближении артефакты становятся очевидными.
С большого расстояния смотрится сносно. Увы, при необходимом для игры приближении артефакты становятся очевидными.
99.7Mb
Вес, текстуры фона одним файлом. Находясь в памяти игры, из-за отсутствия сжатия он занимал бы вдвое больше места.

Удобным вам способом нарежем получившееся полотно на равные картинки с неймингом их порядка по столбцам и рядам. У меня получились 180 картинок разрешением 1024px.

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

How to: Делаем свой Disco Elysium в Construct 3

Будем действовать экономно: нам для всей магии понадобится всего один объект. Создадим Объект (назовем его bg_tile) типа спрайт с 9 кадрами в одной анимации со скоростью 0.

Маску коллизий сделал неровной — в теории это должно помочь эффективнее триггерить переход между ними.
Маску коллизий сделал неровной — в теории это должно помочь эффективнее триггерить переход между ними.

Поскольку кадры этого Объекта будут заменяться, сами они могут быть небольшими, чтобы сцена на старте весила меньше. Добавим ему 2 переменные adr_x и adr_y с порядковыми номерами столбцов и строк для наших «чанков» (в редакторе уже существует понятие тайл, поэтому я буду использовать этот термин, чтобы не путать). Увеличим размер bg_tile и замостим им нашу сцену: сначала расположим их 3 на 3 и проставим каждой копии свой номер кадра, наша задача сейчас подобрать размеры чанков таким образом, чтобы одновременно в кадр попадало всего два чанка по вертикали или горизонтали. Создаем получившимся набором сетку по высоте и ширине количества фрагментов нашего нарезанного фона. Выбираем по одному ряду и столбцу проставим значения переменным adr_x и adr_y.

Если захотите, можете создавать эту сетку циклом при запуске сцены, тогда в редакторе она у вас будет совсем пустая. Но в данном примере оставим как есть, тем более так проще ориентироваться в пространстве.
Если захотите, можете создавать эту сетку циклом при запуске сцены, тогда в редакторе она у вас будет совсем пустая. Но в данном примере оставим как есть, тем более так проще ориентироваться в пространстве.

Добавим спрайт персонажа, которому добавим поведение Scroll to и 9 directions. С приготовлениями закончим, перейдем к логике.

How to: Делаем свой Disco Elysium в Construct 3

Герой сегодняшней статьи: Load image from URL

Это действие заменяет текущий кадр спрайта на картинку по указанному адресу. Мы будем подгружать новые части фона, которые лежат внутри директории проекта. При касании персонажем Чанка вызываем функцию с переменными этого Чанка.

Внутри функции:

подбираем по одному Чанку с соседними значениями переменных

добавляем действие Load Image

указываем путь к нашей папке

и получившимися переменными получаем название нужного файла для полного адреса

сохранением размера оригинального объекта.

Все, давайте посмотрим на то что получилось. Побежали!

Я еще перегенерировал несколько фрагментов в качестве эксперимента, но быстро бросил.
Нужно быть аккуратным с масштабом, потому что Чанки с одинаковыми кадрами дублируют вставленное изображение.
Нужно быть аккуратным с масштабом, потому что Чанки с одинаковыми кадрами дублируют вставленное изображение.

Основную задачу статьи мы уже достигли, поэтому давайте подведем промежуточный итог. Запущенная сцена занимает всего 60 Mb (меньше веса целого файла фона). Эффект можно реализовать даже в бесплатной версии C3 и у вас останется достаточно действий для остальных механик, ведь мы потратили всего 2 объекта и 3 события от общего лимита.

плюсы:

+ оптимизация ресурсов (ресурсы не накапливаются при загрузке)

+ простота реализации

+ кастомизация (тайлы не обязательно делать квадратными или ограничиваться делением на 9 фрагментов)

+ простота редактирования (легко обновлять файлы)

+ дополнительные возможности (можно, например делать бесконечные уровни)

и о минусах:

- нужно ограничивать масштабирование, чтобы не было видно повторение Чанков

- в текущем виде иногда вылезают баги из-за коллизий игрока с несколькими тайлами одновременно

Остальной запас памяти в вашем распоряжении для остальной игры и ее внешнего вида. Чем мы дальше и займемся!

Эффекты

В Диско реализована смена времени суток, которая работает с помощью наложенной текстуры освещения. Здесь можно повторить все предыдущие шаги и создать такую же систему Чанков, но на новом слое, который будет рисовать освещение и тени такого же исключительного качества, как и фон. Однако здесь можно и сэкономить и использовать для освещения меньшее разрешение исходников. Я же сделаю проще и на слое освещения использую всего один большой растянутый спрайт, чтобы не тратить время и заодно показать вам на оценку степень допустимой «шакальности» изображения.

В графическом редакторе я раскрашу еще предапскейленый фон, создам вариацию вечернего и ночного освещения на новом слое. Сразу включаю эффект Multiply поскольку именно его я хочу использовать в игре.

Теперь в сцене добавляю прозрачный слой поверх остальных, создам Объект с новым освещением разрешения 1024px и растяну до 15К совмещая с фоном. Можно оценить изменения.

Вся сцена становится более атмосферной, а весит 100 Mb (в моем случае). По хорошему все тени должны быть на этой текстуре, однако тут на нейронке они уже нарисовались сами. Персонаж, кстати тоже освещается вместе с фоном, увязывая все вместе.

Добавление деталей

Получилось атмосферно, по пока это просто большая картинка, а не город. Поэтому теперь нам все же понадобятся мелкие объекты окружения, которые уже можно анимировать, двигать и расставлять по сцене. Давайте быстро пробежимся по основным методам работы с окружением.

1. Маски

Перед тем, чтобы нагружать сцену объектами, посмотрим, что можно выжать из нашего фона. Например, сейчас герой двигается поверх зданий, вместо того, чтобы заходить за них. Мы можем исправить этот недочет с помощью маски. Перенесем персонажа на новый слой Objects и добавим еще один спрайт, которому выставим эффект Destination Out который будет выполнять роль маски скрывающей персонажа при наложении.

Спрайты маски
Спрайты маски

Результат получается довольно топорный и без 100% совпадения краев геометрии будут заметны артефакты. Но их можно задекорировать другими объектами, расставить преграды для игрока или уточнить маску особым спрайтом, созданным по контуру объекта на фоне. В любом случае объекты масок помогут сэкономить, поскольку их можно использовать повторно и в более низком качестве.

2. Сортировка по Y

Теперь перейдем к добавлению объектов окружения. Загружайте на слой к персонажу подготовленные заранее бочки, ящики и столбы. Все эти объекты нужно добавить в одну семью f_ordering вместе с игроком. Логика события такая: все объекты попадающие на экран при движении персонажа сортируются по порядку друг на друга Sort by Y, относительно их значения по координатам Y (поскольку у нас двухмерная игра с видом сверху ось Y одновременно исполняет роль оси Z).

How to: Делаем свой Disco Elysium в Construct 3

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

*Кстати, объект маски тоже можно добавить в семью сортировки. Это позволит персонажу вставать перед фоном, а потом зайти за него.

3. Освещение с помощью Additive

Освещения мы тоже можем добавить объектами, DE тоже так делает. Однако уже имеющийся слой с Multiply может делать изображение только темнее, а для визуализации освещения исходной яркости фона может не хватать. Поэтому поверх добавим новый слой с эффектом Additive. На нем будут спрайты декалей, добавляющие яркие цвета и пересвет от источников света.

*Кстати, советую декали изначально делать белыми, а потом в свойствах можно менять оттенок для каждой копии отдельно.

Больше оптимизации, больше!

У нас получилась детализированная объектами сцена. Даже, наверно слишком детализированная! Мы вернулись к тому, от чего бежали изначально — куча объектов в сцене тратящих наши ресурсы. Зачем нам держать на уровне не самые важные объекты, если они находятся на другом конце карты?

И тут нам на выручку придет Recreatre initial objects, который переносит расставленные в другой сцене объекты в текущую. Мы даже можем попробовать реализовать это постфактум, если уперлись в производительность по ходу разработки.

Давайте дублируем текущую сцену и удалим с нее все дополнительные объекты, кроме Чанков, перснажа и текстуры освещения. В «сцене доноре», объекты могут лежать скопом, игрок сюда никогда не попадет.

How to: Делаем свой Disco Elysium в Construct 3

Для простоты в рамках обучения, все воссоздаваемые объекты поместим в одну семью f_streaming (будьте аккуратны и по возможности заранее тестируйте этот момент, поскольку не все типы объектов можно поместить в одну семью).

Добавим спрайт-триггер, который запустит подгрузку. Замеряем зону переноса в виде координат прямоугольника и вписываем название сцены донора.

вписывая в <b>Source layout </b>значение <b>-1</b> мы скопирует объекты со всех слоев разом.
вписывая в Source layout значение -1 мы скопирует объекты со всех слоев разом.

Тестируем.

Система триггеров и выгрузки еще потребует доработки, но вполне работает. На мой взгляд это довольно удобная система не только для оптимизации. Вариантов ее применения множество. В перспективе можно иметь несколько версий таких сцен доноров, хранящих объекты для разных событий или условий. Или подгружать объекты только определенного свойства.

Бонус — деревья

Надеюсь статья оказалась вам интересной, несмотря на то что она разрослась в процессе написания. Для тех кто дочитал до конца, хочу добавить небольшой бонус.

Еще до написания этой статьи, до начала работы с С3, до знакомства с роликом я продолжительное время занимался демейком Мора. Утопии. Когда я сел проходить Disco Elysium я зацепился за то, что тамошние деревья, несмотря на общую изометрию вполне себе смещаются относительно камеры, что давало ощущение объема. Тогда я сварганил нечто похожее в своем Горхоне. С тех пор прошло много времени и мой демейк повис в лимбе. Но в память о нем я предлагаю воссоздать эффект тех деревьев, но уже более элегантным способом.

Понадобятся спрайты: ствола с ветками, листва и технический спрайт вершины, для просчетов. Два слоя: обычный и над ним слой tree с параллаксом 105% для кроны и вершины. Совмещаем все на сцене(или генерируем как представлено в примере) и создадим событие, чтобы ствол находясь на слое objects реагировал относительно смещения вершины слоя tree. Для этого добавим событие, чтобы ствол менял угол и высоту относительно точки на другом слое. Для этого в координатах вписываем LayerToLayerX и Y от вершины дерева.

Эффект не бросается в глаза, но деревья смещаются по ходу перемещения по сцене.
2020
11
10 комментариев

М.О.Л.О.Д.Е.Ц.

1
Ответить

Пасиба! Под конец от кривости нейронки подташнивать начало и пост никак было не закончить. Уже и второй вариант начал готовить, но и он при увеличении превращается в кашу.

1
Ответить

Какой же полезный пост! Утащила из него пример про загрузку картинок из файлов в свою игру))

1
Ответить
Автор

Завидное рвение! Удачи с оптимизацией. Для веба данный пример действительно надо тестить. С одной стороны сама сцена не весит много как я и описал. Но с другой, веб игру нужно в начале скачать полностью и лежащие отдельно непожатые файлы будут не в текстурных атласах и могут весить больше.

1
Ответить

Комментарий недоступен

1
Ответить

спаибо, полезно)

Ответить

Очень рад, если так.)

1
Ответить