Разработка игры на Processing. От движка до релиза

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

Processing это отличный инструмент для создания приложений с насыщенной графической составляющей. Он позиционирует себя как язык программирования с Java-подобным синтаксисом. Однако имея за плечами более чем шестилетний опыт взаимодействия с ним я предпочитаю называть его мультиязычным фреймворком. Сам проект Processing представляет собой простейшую среду программирования с прикрученными к ней библиотеками. Среди них следует отметить первую очередь библиотеку core. jar, содержащую в себе все ключевые классы и методы для вывода графики на экран, обработки пользовательского ввода и работы с файлами. Особенность среды разработки Processing заключается в том, что она скрывает от пользователя некоторые части кода и кое-какие тонкости Java синтаксиса, которые могут затруднить начинающему кодеру погружение в мир программирования. Так например простейшая программа для плавной смены цвета окна с черного на синий, выглядит на Processing намного проще, чем её истинное Java-воплощение:

Processing - слева, истинная Java - справа.
Processing - слева, истинная Java - справа.

Использование Processing позволит вам создать свою простую игру даже не зная, что же такое класс, почему некоторые функции помечены как static, что же такое PApplet и почему class расширяет его. И это при том, что под капотом у Processing лежит Java, которая неподвижно стоит на основах ООП и где вы ни одной программы не сможете создать без создания класса и статичного метода-лончера. Мультиязычным этот проект предлагается считать потому, что только официально Processing community поддерживает помимо основной Java-версии, разработку на языке Python, Java-script и R. Далее мы будем говорить только об основной Java-версии и о её брате-близнеце предназначенном для Android разработки.

Если Вы новичок в программировании в целом и в Java в частности, то рекомендуется все-таки начать с загрузки официальной среды разработки. Примеры можно запустить практически в четыре клика и начать изучать код, внося в него свои правки и глядя на изменение в работе. Экспорт приложений для распространения на desktop-платформе также не вызывает затруднений, в отличие от той же LibGDX, экспорт в которой со вшитой Java runtime (jre) потребует немного танцев с бубном. Если же вы знаете толк в Java то работу с Processing можно описать так: в Вашем стартовом классе — наследнике PApplet есть два ключевых метода: setup() и draw() (см. рис. 1). Первый выполняется при старте программы. Второй крутиться в замкнутом цикле, каждый раз по окончании очередного прохода выводя на экран экземпляр класса PGraphic под названием g. Именно в этот оффлайн-буфер мы выводим всю игровую графику. Все классы, которые вы создаёте с помощью официальной среды разработки, являются внутренними классами. Это позволяет вам забыть о модификаторах доступа. Важно тут сказать о следующем:

Processing позволяет Вам выводить графикуна экран как с применением аппаратного ускорения (в режиме 2D или 3D), для чего используется Open GL, так и силами виртуальной машины Java (Software rendering). Также доступна отрисовка с помощью JavaFX. В большинстве случаев для обеспечения высокой производительности следует применять OpenGL рендеринг, однако в десктопных приложениях с большим количеством одновременно отрисовываемых примитивов (линии, точки) и малым количеством скалируемых картинок software rendering может оказаться более выгодным.

Решение засесть за разработку серьёзной для меня видеоигры я принял после того, как увидел встроенные в Processing примеры из библиотеки «Box2D for Processing». Та физика, которую я ранее кое-как пытался симулировать собственной библиотекой открывала необычайный круг возможностей. Потыркавшись с ней решение сформировалось: следующая игра будет использовать Box2D, по жанру будет экшен-платформером (сайдскроллер). Выбор игрового движка стал следующим тяжким вопросом: зная на тот момент только Java и Processing я выбирал между Processing и LibGDX (в ней тоже имеется обертка для Box2D) . Готовые игровые движки я отмел сразу по двум причинам: во-первых, не было интереса к их изучению, ибо игру делаю ради программирования, а не ради продукта. Ценность для меня — процесс программирования и познания внутренностей игрового движка, создание оптимальной его архитектуры. Вторая причина — среда разработки. Основным устройством для создания игры должен был стать планшет/смартфон. Частично можно было задействовать компьютер на работе, однако стоящий там Unity или Unreal Engine вызвал бы вопросы, в то время как Java IDE использовалась мной в одном из проектов для производственников. Её существование на рабочей машине было вполне оправдано. На платформе Android LibGDX может быть использована с помощью AIDE (Android Integrated Development Environment) , где она удачно встроена. Processing имеет как среду разработки для Android — APDE, так и позволяет быть подключённым все к той же AIDE в качестве jar-библиотеки. В первом случае мы получаем быструю компиляцию и запуск. Во втором случае: автодополнение кода, автоматическую генерацию геттеров с сеттерами, конструктора и прочие плюшки профессиональной среды разработки. На Windows ПК разработку я предпочитаю вести в Intellij Idea. Дома была возможность сидеть только за одноплатным компьютером. Unity у меня там кстати так и не встал — может со временем он появиться на ARM устройствах. Однако Intellij Idea работает там без нареканий, ибо создана на Java.

С Processing у меня был большой опыт и с позволения сказать — большая любовь и преданность. LibGDX казался сложнее, хардкорнее, профессиональнее. Несмотря на наличие пяти книг по нему я до 2023 года так и не засел за его изучение. В итоге я все же выбрал Processing. Как сказал Джон Ромеро в одном из своих интервью: лучший язык программирования — это тот, которым ты владеешь лучше всего.

Что касается работы с графикой, то тут все просто: на Windows и Linux GIMP, на Android — Pixel Studio. Собрать Acerpite из исходников у меня не удалось — сказалась видимо нехватка знаний Linux и низкоуровневых языков программирования. Однако это и не требовалось: тут как в большом теннисе: если руки кривые, то ты не увидишь разницы между ракеткой с композитным ободом и кевларовой струйной и сковородкой. Но если ты мастер спорта — ты точно знаешь — что тебе требуется от твоего инвентаря, ты чувствуешь каждую вибрацию инвентаря и точно знаешь, что тебе нужно от ракетки. Так и с графикой, которую я решил сделать в стиле 8-16 битной эры игровых консолей. Я не обладаю художественным талантом и пиксель арт мне показался наиболее простым и доступным способом создания игровой графики, создавать который можно и на стареньком Xiaomi. По факту, конечно, тут оказалось все также, как и с любым другим ремеслом. Чтобы по-настоящему овладеть им потребуются годы.

Design document

Помимо того, что было указано ранее про игру, следует добавить к ней следующие хотелки:

1) В игре будет возможность стрелять под любым углом. Я не наблюдал этой фичи в других играх. В эпоху 8-16 бит это было невозможно из-за аппаратных ограничений. Максимум вы могли в Contra стрелять в 8-ми направлениях. В дальнейшем я перестал играть в платформеры и возможно эта фича была реализована не раз в более современных представителях жанра.

2) В игре будет разрушаемое и разделяемое на куски окружение. Иначе зачем я буду ковыряться с Box2D? Не все конечно, но некоторое должно иметь возможность быть уничтоженным. При этом обломки точно также можно будет разрушать и далее.

3) Игра будет поставляться с редактором уровней.

4) В игре будет возможность замедлять время.

5) В игре будет 7 видов оружия.

Помимо огнестрельного оружия уничтожать противников можно будет также следующими способами:

а) ударами ногой (запинывая)

б) прыгая на врагов сверху вниз

в) быстро движущимися предметами. Например, пнув камень в противника.

г) сбросив врага в яму. Это организовать не просто: нужно либо толкать его с помощью промежуточного предмета, например кирпича, трупа противника, либо приземлившись перед ним, что отбросить врага слегка назад.

Не могу вспомнить, в какой момент времени я выбрал окончательное название игры, но так или иначе в отечественной локализации игра вышла под наименованием «Голубой берет«. Остальной мир получил для ознакомления эту игру с просто переведенным Google переводчиком на английский язык названием "Blue beret".

Processing в деле

Возможность разработки на Android с телефона, конечно, полезна, но с ПК, несомненно, удобнее. Несмотря на то, что релиз на Windows и Linux не был запланирован, было принято решение создать мультиплатформенный проект в Intellij Idea вместо того, чтобы работать лишь над Android версией в Android Studio. Это позволило с одной стороны увеличить скорости компиляции и запуска для тестирования (скорость сборки и запуска зависит от смартфона, но десктоп версия запускается во всех случаях быстрее), с другой стороны немного замедлило скорости работы за счёт небольших различий в библиотеках Processing для Desktop и Android. Зато теперь я намного сильнее прокачался в Gradle. Раньше этот чёрный слоник повергал меня в ужас.

Изначально был создан двухмодульный проект на базе Android проекта. Второй модуль представлял собой модуль настольного приложения. Весь код писался в основном Android модуле, при этом в десктопном модуле был лишь лончер, который создавал окно с Open GL содержимым и набор библиотек, которые подключались на этапе компиляции для создания окна и чёткой его отрисовки на различных настольных операционных системах с различными архитектурами процессоров. Это не совсем логично, так как компилятор в этом случае допускает скомпилировать для настольной версии код, содержащий обращения к специфичным для Android функциям, таким как получение ссылки экземпляр текущей активити, запрос permissions, смены ориентации дисплея и т. д. Кроме того, некоторые константы в Android и Desktop версиях библиотеки имеют разное числовое значение. Из-за этого я долго не мог понять, почему выравнивание текста не работало в Desktop -версии, но отлично работало на Android — оказывается одна и та же константа LEFT имеет значение 31 и 39 соответственно. Так или иначе, но проект в итоге был пересобран под трехмодульный, повторив тем самым структуру проектов фреймворка LibGDX. Имеем три модуля — android, desktop и core. Первые два создают лончеры для соответствующих платформ и далее заворачиваются в интерфейс IEngine и в таком виде отправляются в модуль core ко всей остальной игре. То есть, если на этом этапе ещё не понятно — Processing не представляет общий интерфейс для работы на обеих платформах — он просто предоставляет две библиотеки, где на 99% названия функций, классов, констант и т. д. совпадают. Для тех из Вас, кто не хочет биться головой об стену и боится черных слонов я выложил на GitHub шаблон проекта.

Для разработки на Android лучше всего подойдёт AIDE, однако стоит учесть, что проект с 2016 года заброшен. Не каждую библиотеку удастся к AIDE подключить, а создать приложение на базе Processing мне удалось только форкнув примеры с официальной GitHub странички. Однако так или иначе это крайне полезно — иметь возможность писать отдельные блоки игры на смартфоне стоя на одной ноге (на которой одновременно стоит кто-то ещё) в общественном транспорте. Это, кстати, отличная проверка вашего кода на грамотность архитектурных решений. Если разрабатываемая игра требует одновременного наличия на устройстве всего проекта в актуальном состоянии и не даёт возможности продолжения разработки на планшете в стесненных условиях — задумайтесь. Возможно, архитектура движка вашей игры держится на соплях?

Как дополнительное устройство для разработки я также использовал одноплатные компьютеры. У меня их два Khadas vim3 pro и Raspberry Pi 4B (8 GB RAM). Есть что-то особенное в процессе разработки на слабом одноплатнике на Linux в бесплатном софте, что-то первобытное, что то хардкорное. Ведя разработку игры на них, я открыл для себя две неприятные вещи:

1) Android SDK не существует в версии Linux для архитектуры aarch64. Это значит, что компиляция полного проекта на олноплатнике невозможна. Приходится выдергивать Android модуль и подменять сборочные Gradle файлы.

2) Кривая поддержка Open GL для Linux ARM у Processing. Как было сказано ранее у Processing имеется 4 способа отрисовки графики на экране:

A) Java (software rendering)

B) Open GL 2D (hardware accelerated)

C) Open GL 3D (hardware accelerated)

D) Java FX.

Очевидно, что производительность второго и третьего рендерера заметно выше, чем двух оставшихся. И это так, будь ваш проект запущен на Windows x86-64 или Android. Но на Linux aarch64 мы имеем примерно в 1.4 раза меньшую частоту кадров в Open GL режиме, чем без аппаратного ускорения. Немыслимо и обидно на фоне того, как аналогичные по сложности и архитектуре кода графические приложения на LibGDX просто-таки летают на одноплатниках.

Реализация некоторых механик

Один и наиприятнейших моментов в инди-разработке, это когда тебе на этапе наличия уже играбельного билда вдруг приходит идея фичи, за реализацию которой ты садишься мгновенно и после её оперативного внедрения ловишь дафаминовый приход — как же все-таки она удачно вписалась в игру — как в геймплей, так и в архитектуру! Так вот количество фич, появившихся уже после окончания диздока, просто зашкаливает. Я расскажу о реализации некоторых из них.

1) «умная» камера. Камера в игре имеет три состояния: на игроке, на точке прицеливания, на событии. Последнее в свою очередь возникает тогда, когда игрок оказывается в зоне действия триггеров, которые как правило выдают сообщение на верхнюю панель и перехватывает в эти моменты камеру у игрока, чтобы показать ему что-то важное. Состояние — камера на игроке, обычное для большинства платформеров, здесь активируется только после смерти игрока или его движения со скоростью, близкой к заоблачной (падения с большой высоты) . В основное время игры камера ориентирована в зоне перед игроком на условной вертикальной прямой. Конкретное положение камеры на этой вертикали зависит от угла возвышения оружия. Масштаб же камеры подгоняется под скорость игрока и резко изменяется (камера приближается) при стрельбе. Это ограничивает стремление игрока носиться по уровням с автоматом на перевес — приходиться останавливаться, чтобы оценить обстановку.

Камера движется по вертикальной прямой

2) «Плавная» камера.

Работа камеры в игре

Как будто мало загонов с камерой — я решил сделать её движения плавными. Пытаясь замедлять и ускорять её своими алгоритмами на уровне девятикласника я в конце концов понял — нужно переходить на высшую математику, с которой у меня проблемы. Идея родилась почти сразу — игра должна управлять не камерой, а лишь одним из концов условной пружины растяжения, ко второму концу которой подцеплена камера. Имея аллергию на диференциальные уравнения ещё с ВУЗовских времен я поступил просто. Библиотека Box2D, используемая для моделирования физики в игре, позволяет создать больше одного физического мира. Вторым физическим миром стал мир камеры, как я его назвал — космический. Космический, потому что в нем тоже не действует гравитация. В нем лишь одно динамическое тело — центр взора камеры и одно лишь соединение — пружина. И нет никаких столкновений — никаких расчётов кроме математической модели пружины. Как я ранее уже писал — при смерти персонажа камера перебрасывается на него. Логично, ведь точка прицеливания в этот момент где-то за закрытыми веками героини. Но помимо того, что пружина начинает тянуть камеру к лежащему трупу персонажа — жёсткость пружины резко падает. Это даёт длительный период колебания камеры после смерти героини вокруг её трупа.

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

3) Разрушаемое окружение. Всё объекты на уровне, имеющие физическое тело имеют определённое количество жизней от 0 до 9999. Если количество жизней равно 9999 — такой объект невозможно уничтожить ни одним оружием. Прицел при наведении на него становиться серым. Если же прицел при наведении на объект становиться жёлтым — такой объект можно а зачастую и нужно уничтожить. Характер разрушения различается в зависимости от формы и количества жизней объекта. Объекты круглой и треугольной формы разрушаясь не оставляют после осколков. Если в них хранились какие-нибудь предметы — то они высвобождаются. Если же объект представляет собой по форме прямоугольник, то такой объект разрушается по одному из трех алгоритмов. Если количество жизней объекта было равно 1, то такой объект после разрушения не оставляет после себя осколков, в противном случае идёт анализ соотношения длины к ширине прямоугольника исходного физического тела. Если исходное тело имеет соотношение длины к ширине>2,5, тогда такой объект после разрушения превратиться в два осколка, делящих его вдоль большего размера напополам. При этом ширина этих осколков будет составлять 0,9 от ширины исходного объекта. Суммарная же длина полученных осколков будет составлять 0,9 от длины исходного объекта. Таким образом два осколка при их теоретическом сращивании дадут меньший объект, чем исходный. Также эти два осколка получают определённую линейную и угловую скорость после разрушения их родителя.

А вот если соотношение сторон исходного предмета меньше 2,5, т. е. он более квадратный, то дробление его на осколки происходит по более хитрому алгоритму:

А) Исходный объект делиться на 4 части. Размеры сторон составляют соответственно 0,45 от ширины исходного объекта и 0,45 от длины исходного объекта.

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

В) все части получают линейные и угловые скорости после своего создания.

Принцип разрушения объектов в игре - прямоугольные делятся на 2 части, квадратные на 4, но лишь три из них остаются в игровом мире.

4) Замыливание бэкграунда. В моменты получения сообщений на верхней панели или в моменты перезарядки бэкграунд становиться смазанным. Технически это реализовано следующим образом. Вся графика в игре отрисовывается в 3 виртуальных фрейм буфера. На первый отрисовывается бэкграунд. На второй — объекты игрового мира. На третий — верхняя и нижняя панель, а также надписи с названием уровня при запуске миссии. Графика бэкграунда для каждого уровня упакована в один атлас текстур (картинку) . При создании уровня в память загружается эта картинка и создаётся её копия. Эта копия сжимается в три раза и тут же растягивается до прежних размеров со включённым антиалиазингом. В дальнейшем именно она используется при отрисовке бэкграунда в те моменты, когда пользователь получает сообщение на верхней панели.

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

5) Освещение.

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

А) смена основного рендерера игры с P2D на P3D. С точки зрения кода это изменение в одной первой строчке кода, но с точки зрения игры — это уже переход в 3D.

Б) смена способа вывода фрейм буфера с объектами игрового мира на экран. При обычном способе отрисовки вывод на экран происходит с помощью функции image() , куда передаётся ссылка на фреймбуфер и координаты его вывода на экран. Я же использую функции texture() и vertex() . Это позволяет не просто вывести на экран картинку, а «натянуть» её за углы на определённую поверхность (в моем случае на несуществующую прямоугольную плоскость, перпендикулярную плоскости экрана) .

В) В момент выстрела помимо стандартного ambient освещения добавляем источник точечного желтого света над местом теоретической вспышки. Таким образом в точке с теми же координатами X и Y но с отрицательным Z.

Весь перечисленный перечень мероприятий был проведён сравнительно быстро, если не считать попыток вместо точечного источника света pointLight() использовать spotLight() . Последний не заработал из-за бага Processing, который не отображает световые пятна на плоскостях. Как результат — при выстреле и взрыве весь игровой мир заливается жёлтым сиянием, при этом бэкграунды не освещаются, так как по-прежнему выводятся на экран как картинки.

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

6) Slow Motion/Bullet time. В игре можно найти или купить шприцы с зеленоватым содержимым, после употребления которых игра замедляется. При этом игрок замедляется приблизительно в два раза, а все остальное в четыре раза и пули становятся видны. Большой пользы от этого режима ждать не стоит, пожалуй, лишь до 6-ой игровой зоны, а то и вовсе до первого босса. Добавление этой фичи на этапе уже работающего прототипа потребовало сильно расковырять нутро игрового движка, и я до конца не уверен, что все аспекты игры правильно работают в этом режиме. Однако здесь интереснее сказать не о реализации механики, а о реализации визуального сопровождения эффекта замедления времени в игре. Графически это тоже источник света — только зеленого + шлейф с предыдущих кадров. Тут вскрылась ещё одна неприятная сторона Processing — мне не удалось добиться одинакового отображения режима Slow Motion в Android и в Desktop версии. И там, и там для отображения этого режима используется источник зелёного цвета перед игровым миром. Просто посмотрите на то, как этот режим выглядит на Desktop.

Замедление времени в игре - это был скорее вызов для меня, как для разработчика движка, чем полезная фича. Хотя некоторые уровни было бы проще проходить именно в этом режиме.

Релиз

Релиз на Desktop не планировался изначально, отчего я решил сразу сделать игру в вертикальной ориентации, что напоминает игру на консолях эпохи NES которая давала почти квадратное разрешение. Первым релизом был nashstore. Тут в принципе все оказалось просто, понятно и незамысловато. Единственное неудобство оказалось в том, что первая версия игры в сторе воспринималась Android системой как вредоносное ПО. Мои наезды на техподдержку были неоправданы — стоило немного подождать и обновить версию одной важной библиотеки и все заработало. Далее через полтора месяца я выложил игру на itch.io. Тут все оказалось ещё проще — даже не нужна политика конфиденциальности. Через семь месяцев я неожиданно для себя протолкнулся в RuMarket. У них крайне необычная консоль оператора, однако выложить игру туда оказалось крайне просто, но позже я обнаружил одну особенность стора: каждый раз выкладывая новую версию Вам нужно увеличить код версии. Причём речь здесь не о том номере, который в сборочном Gradle — файле помечен как versionName, а о том, который помечен как versionCode, на который я ранее не обращал внимание.

Эта переменная должна расти каждый раз, когда Вы обновляете версию в rumarket
Эта переменная должна расти каждый раз, когда Вы обновляете версию в rumarket

Следующий стор, любезно согласившийся принять игру к публикации, был Huawey AppGallery. Тут оказалось, что они не разрешают физлицам прикрутить Huawey рекламу к своим приложениям. Пришлось дальше использовать Яндекс рекламу. Также благодаря Huawey AppGalery я узнал, что такое signed release apk и чем он отличается от тех debug apk, которые я раскидал по всем остальным сторам. Причём чтобы это узнать, пришлось писать в техподдержку после того, как моя игра не прошла предварительную проверку, а причина отказа была настолько невнятно объяснена в ответном электронном письме, что даже Google мне не помог. После подписи и сборки релизной версии я все же выложил игру. Боялся, что придётся какие-то документы писать, сканировать, отправлять в офис, но ничего этого не пришлось делать. В адекватные сроки игра появилась в сторе. Следующим рубежом стал для меня Xiaomi GetApps. Тут в принципе не было никаких проблем, но я допустил критическую ошибку — я решил выложить туда debug-версию, ибо стор её принял без вопросов. Никогда не делайте этого. Далее я объясню, чем это чревато.

Позднее на DTF мне попалась статья о публикации инди игры в rustore. Я немного удивился этой возможности, потому что ранее её не было (или мне так казалось) . Из-за поддержки Минцифры rustore должен был стать эдаким главным стором для российского среднестатистического пользователя (не геймера) , чтобы тот мог своевременно обновлять критичные и важные приложения после введённых санкций и физлицу со своей индюшатней туда вход был закрыт. Теперь же мне удалось добраться и до них и засесть за публикацию игры в этом продукте от VK. С этим стором было больше всего косяков со стороны магазина. Было два критичных бага в магазине, которые сильно повлияли на количество загрузок в игре. Не хочу лишний раз циклится на них — надеюсь никому больше не придётся встретиться с такими же ошибками, как у меня. Но скриншот, посвященный второму из них, все же выложу — вас в нем ничего не смущает? Там внизу еще несколько отзывов и я вас уверяю — это отзывы людей, именно поигравших в игру.

Вас ничего не смущает? Если нет, то покажите мне пожалуйста другой стор, где можно ставить оценки и писать комментарии не играя в игру. У меня есть знакомые люди, которые умеют лайкать
Вас ничего не смущает? Если нет, то покажите мне пожалуйста другой стор, где можно ставить оценки и писать комментарии не играя в игру. У меня есть знакомые люди, которые умеют лайкать

Однако благодаря именно rustore я получил отзыв, который мгновенно убил всю эйфорию от цепочки успешных релизов — игра не работала на Android 11. Как потом я выяснил — не работала именно релизная сборка, ибо дебаг сборка запускалась без проблем. Источник проблем удалось найти за два-три дня и был он в самом Processing версии 4. Возврат к версии Processing 3 решил проблему. Глобальная разница в этих двух версиях заключается в версии Java. Processing 3 использует Java 1.8 в то время, как Processing 4 — Java 17. Что именно мешало Processing 4 запуститься в состоянии release на Android 11 — не понятно и разбираться не стал. Следующим майлстоуном стала оперативная замена всех версий во всех сторах на исправленную. А вот Xiaomi GetApps отказалась заменять версию. Ни релизная ни дебаг версии не воспринималась консолью разработчика. Кривой перевод с китайского на английский, а потом мной выполненный с английского на русский пытался до меня донести, что надо удалять приложение из стора и заливать заново. И сделать это нужно не кликом мыши по соответствующему GUI button, а написав письмо на электронную почту поддержки разработчиков, приложив фото документов, используемых при регистрации аккаунта разработчика. Но стор обещал, что количество закачек и отзывы не пропадут. Как оказалось, или как я понял, беда была именно в том, что изначально я загрузил именно Debug версию, подписанную Debug ключом. При попытке залить любую другую сборку стор оказывал, воспринимая новую как совершенно иную, видимо из-за несовпадения подписей. После выкладывания release версии проблемы больше не наблюдалось, ибо игра подписывается с тех пор одним и тем же ключом.

Следующей целью стал taptap. Он обещал выложить игру и дать возможность ей быть доступной по всему миру включая Японию, Китай и Корею. Сложности тут были связаны со следующими моментами:

А) Консоль оператора заточена под китайский язык, и предлагаемая ими версия сайта на английском обладает набором крайне специфических терминов, назначение которых не всегда ясно

Б) требовалась картинка с разрешением 1920х1080 с изображением героя игры и названия (promo Image).

В) требовалось перевести название игры и описание на японский, китайский и корейский языки.

В остальном стор отработал как положено.

Последний стор, куда была предпринята попытка протолкнуть игру, был немного немало сам Амазон. Тот путь, который мне пришлось проделать, чтобы игра оказалась там, запомнился наибольшим количеством ярких моментов. И дело тут вовсе не в том, что он был последний и воспоминания ещё не были затерты, а в том, какие шаги пришлось предпринять, чтобы игра прошла одобрение. Первое, что потребовал от меня Amazon, а я не был готов, это Promo Image. Требования те же, что и TapTap — картинка с персонажем и названием игры, вот только разрешение весьма необычное -. Сильно не придавая этому значения я на скорую сжал Promo Image от TapTap и срезал лишнее по длинным сторонам. Ответ от Amazon приходит менее чем через сутки и сообщает об отказе в публикации, аргументируя это тем, что моя игра содержит сомнительную иконку и название, которое совпадает с другим приложением из Amazon, при этом описание приложения сильно отличается от того, что использовано в описании другого приложения, что может ввести в заблуждение покупателей. Кратко пробежавшись по каталогу я расшифровал отказ стора следующим образом: поскольку Амазон это не только Android store но и свалка промтоваров (как Wildberries или Ozon) по запросу «голубой берет» пользователь получит 200 товаров, из которых 99,9% будут описаны ключевыми словами: гипоалергенный материал, натуральный хлопок, впитывающая поверхность, а мой 0,1% при том же названии и схожей картинке предлагает хардкорный экшен-платформер, 8-битный стиль, 7 видов оружия и прочие неожиданности.

Когда пытаешься найти свой продукт в сторе и понимаешь, что конкуренция велика, как никогда...
Когда пытаешься найти свой продукт в сторе и понимаешь, что конкуренция велика, как никогда...

Долго не думая я переименовываю игру в Blue Beret Begins (трипл Би) , перерисовываю Promo Image, делаю новую иконку и отправляю обратно новый билд игры. Ответ пришёл в течении суток и нёс в себе ту же информацию об отказе и почти ту же причину отрицательного ответа стора. Решив, что добавлять определенный артикль the к названию бессмысленно я решаюсь на радикальный для себя шаг: я переименовываю игру в Girl of War и возвращаюсь к первой (основной) версии иконки. Выдохнув, скрестив пальцы, со словами «с богом и включённым VPN» я шлю новый билд игры в самое сердце загнивающего запада.

Girl of War. Chains of the airborne forces
Girl of War. Chains of the airborne forces

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

Бессовестный плагиат это плохо, а неосознанный - просто отвратительно
Бессовестный плагиат это плохо, а неосознанный - просто отвратительно

Оказывается, вражеская нейросеть сочла, что моя игра нещадно эксплуатирует образ одной героини японских аниме-комиксов. Кратко пробежавшись по результатам поиска в интернете по запросу «Tanya von Degurechaff» я осознал всю комичность ситуации и любезного предложения Amazon — приложить документ, удостоверяющий право на использования образа этой героини или же сам документ, подтверждающий, что образ героини принадлежит мне. Решив не сдаваться и далее нашему геополитическому врагу, я начинаю рассуждать трезво (впрочем, как и всегда) — нейросеть познакомилась с материалами игры и какой-то из них сильно похож на Таню. Скорее всего речь идёт о картинках в заставке. Можно их попробовать перерисовать, однако это долго и не факт, что с первого раза удастся создать нечто похожее, но менее плагиатное. Но можно их попробовать скрыть. Если нейросеть просто открывает APK- архив и шерстит файлы, отыскивая среди них графические — то этот способ сработает. Можно было попробовать просто сменить расширение, или запихнуть картинки в запароленные архивы, которые при запуске игры разархивируются в папку с приложением внутри Android-директории. Однако я поступил по-другому. Ещё раньше во время разработки игры я придумал свой формат хранения графических файлов -. mgdstexture. Формат текстовый и хранит информацию о графическом файле в виде букв и цифр, что до неприличия сильно раздувает объем файла, однако исключает возможность просмотреть его любым графическим редактором. Поскольку к формату был давно придуман как расшифровщик, так и зашифровщик, пересохранить все картинки из intro в свой формат оказалось не только просто, но и быстро. Собрав новый билд, я решаю ещё раз бросить вызов Амазону и. … наконец одерживаю победу. Игра выходит в течении суток и я, увлекшись сражением с нейросетью Амазона, понимаю, что забыл поменять скриншот с главным меню игры, где остался оригинальный логотип игры.

Тот самый, как мне кажется, наиболее бессовестный плагиат, скрыт от вездесущего ока нейросетей Амазона.
Тот самый, как мне кажется, наиболее бессовестный плагиат, скрыт от вездесущего ока нейросетей Амазона.

Выводы

Подумать только, когда то в начале игра выглядела так...

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

1) Processing неплохой инструмент для разработки игр и обучения программированию одновременно. Позиционируя себя как язык программирования я все же предпочитаю называть его мультиязычным фреймворком для создания приложений с насыщенной графической составляющей. Имея большое количество только официальных реализаций — Java, Python, Java Script, R и ещё несколько неофициальных реализаций (напр. C++) он даёт Вам возможность выбрать наиболее родственный для Вас инструмент и используя один и тот же набор классов, функций констант и интерфейсов заниматься тем, что принято называть словосочетанием Creative Coding.

2) Для начинающего я предлагаю выбирать именно базовую версию (Java). Это связано как с большей стабильностью этой версии, так и с большим количеством литературы. Лично мне больше всего помогла книга Эрика Бартмана Processing (скорее всего, не всем она подойдёт) но также очень рекомендую книгу о непосредственно разработке игр - Introduction to Game Development: Using Processing.

3) Processing (базовая версия) является Java лишь под капотом. Среда разработки скрывает от программиста часть кода, которая была бы сложна для понимания начинающему разработчику. Так, например Вы не обязаны ставить букву f после присвоения числового значения переменным типа float. Вы можете написать свой клон Space Invaders даже не узнав, что такое class. И это при том, что мы все же работаем с Java, в которой любая программа содержит минимум один класс. Ежели Вы все же снизойдете до ООП, то все созданные Вами классы, интерфейсы и перечисления будут вложенным классами основного. Это означает, что не нужно думать ни о модификаторах доступа, ни о том, как обеспечить доступ к функциям Processing отовсюду — сохранить экземпляр в статичную переменную или передавать в каждую функцию по ссылке в качестве параметра. Даже само ключевое слово static Вы не встретите — точка входа в любую настольную Java-программу (статичная функция main с массивом параметров командной строки на входе) скрыта от программиста (см. рис. 1). Также вполне очевидно отсутствие пакетов — ключевое слово package Вам не потребуется. Тем не менее несмотря на определённую ограниченность функционала Вы можете писать Java код в файлах с расширением. java и использовать их в проекте. Вот только экспортировать Ваш проект в Desktop-приложение при этом не удастся. Для этого все Ваши. java файлы нужно сперва запаковать в. jar библиотеку, для чего все же удобнее обратиться к серьёзной. java IDE.

4) Если Вы не новичок в программировании и знакомы с ООП — разумнее установить Eclipse или Intellij IDEa и прикрутить к пустом проекту корневые библиотеки Processing. Создав в Processing IDE шаблонный проект и экспортировав его для любой из Desktop — платформ Вы обнаружите в папке экспортированного проекта исходный код класса — лончера. Именно его следует использовать в стартовом классе профессиональной Java IDE. Если не хочеться биться головой о стену - шаблон мультиплатформенного проекта в открытом доступе

5) Потребление памяти. Я проводил тест создав две одинаковые игры на Processing и LibGDX. Обе отрисовывают графику посредством OpenGL. Я следил за ростом потребления RAM. Процессинг за секунду увеличивал потребление оперативной памяти на 10 мегабайт. LibGDX же за 10 секунд увеличивал тот же показатель на 0,1 мегабайт. Понятное дело, что в обеих случаях это не приведёт к утечкам памяти и сборщик мусора отработает как положено. Вот только общее время работы сборщика мусора в единицу времени игры окажется у Processing значительно дольше. У меня в игре на некоторых уровнях сборщик мусора вызывался каждые 0,8 секунд на Huawei Y5P и это логично сажало производительность.

6) Processing for Android и Processing это не совсем одинаковые проекты. Под капотом мы имеем две разные ключевые библиотеки, пусть и состоящие из одноимённых классов, интерфейсов, констант и т. д. И разница тут даже не в том, что в Desktop библиотеке есть методы для выбора стиля курсора и положения окна на экране, в то время как в Android библиотеке методы для запроса Permissions от игрока, а в том — что одни и те же вещи реализованы по-разному. Так, например, чтобы получить адрес проекта в Desktop-версии вы обратитесь к функции sketchPath, а в Android-напрямую к значению переменной. Константа LEFT, используемая для выравнивания текста, имеет в обеих библиотеках разные значения (ранее я писал об этом) . Те баги отрисовки, которые могут появиться в Desktop-версии не окажутся в мобильной версии и наоборот.

7) Сломанная поддержка OpenGL для ARM-одноплатных компьютеров. Для меня это оказалось критично, так как дома валяются два одноплатных компьютера и за ними я могу проводить больше времени, чем за полноразмерным ПК. Было бы не так больно, если бы Processing community не заявляли о наличии особой версии Processing for Pi, которая отличается лишь прикрученной библиотекой для работы с входами и выходами одноплатных компьютеров.

8) Разработку игр на Processing можно полностью перевезти на Android планшет. В какие-то периоды это был единственный мой путь вперёд, ибо не было времени за ПК. Графика создавалась и корректировалась в Pixel Studio, коддинг, компиляция и тестирование в AIDE. Уровни в принципе создавались целиком на планшете, ибо редактор я создал свой. Каюсь правда, что не допилил его до конца — он не очень удобен в использовании. Конечно все это вместе долго и неудобно, но, если выбирать между разрабатывать в неприятных условиях и не разрабатывать вообще — я выбираю первое.

Главный же для себя вывод по всему проекту оказался, пожалуй, самым неожиданным. Делать свою игру в одиночку с нуля, конечно, интересно, но наверняка у каждого (у меня точно) есть какие-то части этого пути, какие-то сферы ответственности, которые нравятся больше, а какие-то меньше. И именно разработка архитектуры движка и редактора уровней оказалась наиболее интересным этапом разработки лично для меня. Остается скучать и вспоминать времена, когда каждая игра, появлявшаяся на рынке, была сделана на своем уникальном движке, со своими особенными, багами, с камерами, влетающими за текстуры, с невидимыми стенами, с кривыми прыжками. Когда открывая папку с игрой ты мог видеть редактор карт и начать с ним ковыряться, пытаясь создавать свои карты в Противостояние 3, устраивать расстрел с воздуха всего и вся на Колгуеве в Operation Flashpoint, делать своих персонажей с помощью TES Construction Set для Morrowind и т.д.

1313
15 комментариев

С ума сойти. "Безумству храбрых поём мы песню", как говорится. У меня немало опыта с Processing (ещё с версии "1.сколько-то там"), но никогда не приходило в голову сделать на нём полноценный релизный продукт — всё же он для меня воспринимается исключительно как среда для разработки прототипов.
Тем не менее, поздравляю с завершенной разработкой и релизом!

2
Ответить

Премного благодарен!

Ответить
1
Ответить

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

Ответить

Вы про статью или про игру? В любом случае принимаю за комплимент

1
Ответить
Ответить

Понимаю, вероятно стоило часть статьи вытряхнуть на Хабр. Постарайтесь найти для себя интересный раздел по Вашим предпочтениям.

Ответить