Попытки понять Godot
Я всегда пишу свои игровые движки для своих игр. Я устал объяснять окружающим, почему, ведь мнение о том, что свой движок писать нельзя, тиражируется на каждом углу. Я писал свои движки до того, как родился тренд на готовые решения и понимаю, зачем это нужно мне и моим проектам, и почему другие профессионалы часто делают этот выбор.
Но незатыкающаяся народная молва о том, что на готовом-то движке проще и эффективнее, ведь его десятилетиями пилили лучшие умы человечества, а не я, делает своё дело, и время от времени я проверял, так ли это: пробовал осваивать GameMaker, Unity, или, вот, Godot. Без особого успеха, для моих целей всё это оказывалось не применимо.
На днях возник очередной раунд: я решил попробовать освоить Godot. Причины две: я решаю, стоит ли перепиливать старые наработки для возможного нового проекта, или же готовый движок действительно эффективнее; и также мне нужно получить экспертизу, чтобы иметь возможность ответить на вопросы, касающиеся другого проекта.
Проекты — простые 2D игры с пиксельной графикой, аутентичные 8-16 битной игровым эпохам. Соответственно вопрос я рассматривал именно с этой точки зрения. Думаю, для игр иного рода, более современных или 3D, выводы будут иными. Но у меня конкретный практический интерес: решается моя задача или не решается.
Скачал последнюю стабильную (это важно) версию 4.7.2 и начал тыкаться, пытаясь создать примитивное подобие Super Mario Bros: простой уровень, перемещается персонаж, взаимодействие с фоном. По идее, движок, который разрабатывается уже 12 лет, и применялся в реальных проектах, без проблем может решить эту задачу за считанные часы. Совсем без движков это делается за день. Спойлер: я потратил три дня, получил крайне негативный опыт, и задачу не решил ни в каком виде.
Мои ожидания: движок решает базовые задачи — подготовку и организацию ассетов (наборы тайлов, карты уровней, наборы спрайтов), базовую физику, столкновения, возможно камеру. Вывод на экран, в конце концов. Я же пишу игровую логику на скрипте. При этом движок должен дать мне достаточно контроля, чтобы игра управлялась так, как я хочу: это главное, что разработчик создаёт в играх подобного рода, игровой опыт, конкретное узнаваемое ощущение от взаимодействия с игрой.
Проблемы начались сразу же на первом шаге, и не прекращались никогда. Первый шаг: масштабирование пиксельного буфера до размеров окна или экрана. Базовая опция в настройках проекта. Устанавливается и благополучно не работает. Сломалась сколько-то версий назад, никто не чинит. Вопрос решается написанием стартового скрипта, который принудительно выставляет настройку программным образом после запуска проекта.
Но это мелочи. Ладно. Создал тест: объект-квадратик с координатами X,Y движется по двум осям по нажатию кнопок, на один пиксель в кадр (хоть с дельтами, хоть без). И что я увидел: если двигаться по одной оси, движение идеально плавное. Если по двум, то есть по диагонали, явно наблюдается движение ступеньками! Как оказалось, это проблема стандартного округления float-координат при отображении. Чтобы убрать этот эффект, нужно раздельно хранить мировые и экранные координаты объекта, и округлять вручную. Тогда эффект ступенек, которого не должно было быть изначально, пропадает.
Далее: нужно создать карту тайлов. Для этого есть редактор. Создал карту, загрузил набор тайлов. Нажал на другой объект в списке, и карта исчезла без следа. Редактор не посчитал нужным её сохранять. Потому что если созданный файл не сохранялся, он не считается изменённым. Стандартное поведение 100% софта на свете в такой ситуации — предложить сохранить файл. Godot молча выбрасывает результат твоей работы в помойку, сам виноват, надо было сохранять сразу после создания.
Далее про то, как работают столкновения и физика в движке. Внезапно, применяется реальный физический движок (типа Box2D, но собственный), который проверяет столкновения как пересечение полигональных объектов. Очень ресурсоёмкая и избыточная затея для примитивной 2D игры. Но и пусть. Если бы это работало. Но это не работает принципиально, на уровне математической концепции: каждый тайл уровня считается отдельным полигоном. В результате персонаж, идущий по плоскости, имеет ненулевой шанс запнуться об стык полигонов двух соседних тайлов. Решение? Сделать персонажа капсулой, полигоном с закруглёнными углами. Это умножает вычислительную сложность, но… лишь снижает шанс запинания. Решение? Оставить всё как есть.
И я могу понять этот подход в применении к 2D/3D играм со сложной изначально полигональной геометрией, где стыки полигонов — пограничный случай. Но в Super Mario Bros 1985 года на 1.79 МГц никаких запинаний в движении не может быть в принципе, а на в тысячу раз более мощной системе предлагается принять их за норму. Но я никаким образом не могу принять такую норму. Для меня это очевидная проблема.
К тому же, стандартная система не является детерминированной: TAS (или просто демка геймплея через запись ввода) на ней невозможен, столкновение может срабатывать по разному. Тогда другое решение: предлагается вручную написать своё AABB столкновения с tile probe в стиле олдскульных игр. То есть движок внутри движка. И так все и делают: разработчики признают, что Godot плохо подходит для классических 2D игр с тайловыми картами, и только написание собственной замены стандартному решению действительно работает.
Ладно, допустим. С базовым классом (который ещё разберись как делать, если ты новичок) собственная реализация физики и столкновений занимает несколько сотен строк. Конечно, движок теряет свою полезность для проекта, не выполняя базовую задачу, но хотя бы в нём можно редактировать карты, с визуальным и интерактивными слоями (маска столкновений).
А вот нельзя. Редактор карты заточен только на стандартную систему. И это полигональная маска для каждого визуального тайла. То есть если нужно, чтобы графика одного и того же тайла применялась с разным флагом проходимости (для секретных проходов, например) — этот тайл нужно дублировать.
Стандартная практика в играх — разделить визуальный и интерактивный слой, типа walkmesh. Но в Godot такого нет. Тут предлагается создавать отдельный визуальный слой тайлов, в который загрузить изображения своих флагов проходимости. Либо один пустой тайл, и назначить ему цветную полупрозрачность. В рантайме отображение слоя отключается. И если в игре десять видов флагов (проходимость, платформа. лёд, вода, огонь, шипы)... делайте десять слоёв, и пытайтесь угадать, что означает непонятный оттенок цвета, получившийся при их наложении.
Это, конечно, дичь, и разработчики это понимают. Поэтому предусмотрено другое решение: скрипты для расширения возможностей редактора. Можно скриптом обрабатывать дополнительные атрибуты ячеек карты и выводить на экран надписи типа «вода», «огонь». Но скрипт надо сначала написать, а для этого нужно понимать внутреннюю архитектуру и движка, и редактора.
Я уже молчу про крайне неочевидный интерфейс. Например, в один момент времени можно наблюдать на экране пять иконок «три точки», за одной из которых спрятана критически нужная функция.
В итоге все проблемы решить можно. Но это постоянная доработка движка и его редактора собственными силами, причём максимально неудобным и непонятным способом: вместо того, чтобы делать свою игру или свой движок, нужно копать тонны документации или просить помощи ИИ. А так как версии Godot ломают совместимость, перемещая элементы управления и переименовывая функции, в любом случае это довольно неприятная задачка.
Возникает вопрос: ради чего всё это? Использование движка обещает упрощение процесса, ускорение работы. В итоге я в очередной раз потратил немало времени, и фактическую работу так и не начал, пытаясь разобраться в особенностях инструмента, который из коробки требует допиливания нетривиальных костылей. И получается, что я дважды дурак: и что пишу свои движки, и что не могу осилить готовый популярный и весь такой классный.
Похоже, как говорится, я просто слишком стар для этого дерьма.
рабочее
разное
кряхтение
Хижина Джедая
Минусов больше, чем плюсов.
Oct 28 12:23