Уроки, извлеченные из портирования игры

Недавно я портировал раннюю версию своей 2D-игры Cove Kid с Mac на Windows.

У некоторых из вас может сложиться впечатление, что на это должно уйти как минимум несколько месяцев, верно? Готов поспорить, что это заняло у меня шесть месяцев? Может ли такое число показаться необоснованным?

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

В моей игре Cove Kid около сорока головоломок, несколько уникальных игровых механик, небольшой саундтрек, специальные звуковые эффекты и довольно много анимаций. Мне понадобилось восемь месяцев, чтобы зайти так далеко.

Итак, сколько времени мне понадобилось, чтобы перенести его на Windows? Вы бы поверили мне, если бы я сказал вам это заняло всего две недели?

Это невозможно, скажете вы! Я говорю вам, что это не так, и я здесь, чтобы пролить немного света, убедить вас по-другому взглянуть на то, что означает кроссплатформенность приложения или игры.

Как это было в старые времена

В 1960-х и 1970-х годах мир вычислений сильно отличался от мира, который мы видим сегодня. Персональных компьютеров не было, и у каждого устройства был свой набор программируемых инструкций. Мы называем эти наборы инструкций языками ассемблера.

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

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

C — кроссплатформенный язык. Его можно скомпилировать для запуска на чем угодно. Именно так мы делали кроссплатформенные приложения и разработку игр. Это до сих пор делается во многих игровых студиях, которые делают свои собственные движки (например, моя).

Если вы используете такой игровой движок, как Unity/Unreal, он выполняет кросс-компиляцию различных библиотек C/C++, чтобы ваша игра работала на поддерживаемых ими платформах.

Как это сейчас

Удивительно, но это не относится ко многим кроссплатформенным приложениям, созданным сегодня.

Slack работает внутри Electron, который по сути представляет собой полноценный веб-браузер, который просто выглядит как контейнер приложения. Slack имеет кроссплатформенную кодовую базу, но кроссплатформенным языком является Javascript.

Поскольку им приходится поставлять полноценный веб-браузер только для того, чтобы запускать их Javascript, библиотеки NodeJS и все остальное, что с ним связано, вы можете ожидать, что такое приложение, как Slack, будет довольно раздутым. И вы были бы правы! Он и раздутый, и медленный.

В принципе, Slack можно было бы переделать, чтобы он стал кроссплатформенным для такого языка, как C++. Нет технических барьеров, препятствующих этому. Но этого не происходит. Почему?

Если бы он был кроссплатформенным на таком языке, как C/C++, выигрыш в производительности был бы огромным! Он мог бы работать в тысячи раз быстрее и потреблять меньше ресурсов. Назовите любую функцию, которую приложение в настоящее время поддерживает, и это будет намного лучше.

У нас похожая проблема с мобильными приложениями. Многие кроссплатформенные мобильные приложения в настоящее время создаются с использованием инструмента под названием React Native.

React Native — это, пожалуй, самый сложный фреймворк для разработки фронтенд-приложений из когда-либо существовавших, и не только потому, что он также использует Javascript в качестве кроссплатформенного языка.

React Native сложен, потому что он пытается создавать приложения на вершине шаткой башни абстракций.

Позвольте мне уточнить.

Создателям SDK все равно, нужна ли вам поддержка нескольких платформ

Когда большинство людей делают мобильные приложения в наши дни, они не начинают с нуля. Обычно они используют SDK.

Пакеты SDK хороши, если вы хотите быстро вывести что-то на экран, но они страдают от привязки к платформе. Это почти наверняка намеренно со стороны создателей SDK.

Apple хочет упростить создание приложений для iPhone, но им все равно, нужно ли вам взять свое приложение для iPhone и разместить его на другой платформе. Они ничего не получают от этой сделки. Это только делает другую платформу более похожей на жизнеспособную альтернативу iPhone.

Apple/Google/Microsoft не выиграют, если научат вас делать ваши программы по-настоящему кроссплатформенными. В результате почти все, что вы создаете, слишком сильно зависит от их SDK, будет сложно перенести на другую платформу.

Есть два способа решить эту проблему.

  1. Используйте SDK очень экономно. Используйте только те части, которые отвечают за отрисовку графики, получение входных данных и вывод звука. Низкий.
  2. Создайте кроссплатформенный уровень поверх SDK. Попробуйте объединить различные абстракции пользовательского интерфейса в SDK со своими собственными абстракциями. Иди высоко.

Мой игровой движок выбрал первое. React Native представляет последнее.

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

React Native, напротив, представляет собой высотную башню с шаткой средней частью. Средняя часть (также известная как SDK) постоянно меняется, и поэтому трудно удержать верхнюю часть (ваше приложение) от падения.

Мне не нужно говорить вам, что React Native сложен. Его сложность является следствием проектных решений, принятых их командой. Они предпочли использовать SDK вместо того, чтобы меньше полагаться на них.

Почему было так легко портировать мою игру?

Это просто. Все, что мне нужно было сделать, это подключить рендеринг, ввод, немного файлового ввода-вывода и звук.

Мне не нужно было менять какую-либо логику моей игры в соответствии с какой-либо конкретной платформой, потому что я с самого начала разработал логику игры так, чтобы она была полностью кроссплатформенной.

Логика моей игры выполняет несколько простых задач. Получите входные данные, смоделируйте некоторое состояние, а затем выведите несколько команд рендеринга и звук для воспроизведения.

По дизайну моя игра взаимодействует только с определенными платформами в нескольких ключевых местах.

Из-за этих превосходных дизайнерских решений перенос моей игры с Mac на Windows был больше похож на подключение одного и того же шланга к другому патрубку, чем на разработку нового шланга.

Можем ли мы сделать то же самое в мобильных приложениях?

Ответ однозначный да, но с сильной оговоркой. Мне потребовались годы практики, чтобы стать достаточно компетентным в C++, чтобы реализовать этот дизайн. Я не собираюсь лгать об этом. Это требует квалифицированного и прагматичного архитектора.

Тем не менее, это было не так уж сложно, и это такая работа, которую нужно сделать только один раз.

Я знаю, что портирование моей следующей 2D-игры займет гораздо меньше времени, потому что теперь, когда я написал этот код, он у меня навсегда. Я могу использовать его во всех будущих проектах.

Я также не собираюсь делать вид, что приложения и игры имеют одинаковые требования. Они явно не знают.

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

Тем не менее, это в пределах досягаемости. Мы можем взять те же приемы из игр и применить их к приложениям. Приложения должны быть более легкой областью проблем, чем большинство игр.

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

Если бы у нас была открытая альтернатива SDK, и она получила бы широкое распространение, это заставило бы FAANG предоставить нам больше межплатформенной поддержки.

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

Современная кроссплатформенная разработка приложений — это позор. Так быть не должно. Мы можем сделать это лучше.