Flappy Bird на Си для Android весом APK всего 92 Килобайта

от автора

Приветствую всех!
Невозможное возможно, сегодня об этом и будет статья.

История

Всё началось в далёком 2021 году.
Тогда я наткнулся на репозиторий rawdrawandroid. Появилась мотивация сделать какую-нибудь игру с минимальным весом APK, но при этом, что бы игра была простой и понятной. В моменте появилась идея сделать клон давно забытой игры Flappy Bird. Которую уже портировали на многие языки программирования.
Тогда, позднее в 2021 году, я нашел ещё один интересный репозиторий Raylib.
Но, первая попытка сделать эту игру была на C++, при использовании ImGui, потому что я уже был с ним знаком.
А так, все трудности были представлены в Android Native Activity и сборке чистого APK из apktool.

Первая попытка потерпела крах.
Во-первых, вес APK был примерно 1 Мегабайт.
Во-вторых, могли случаться вылеты игры.
В-третьих, внутри APK была только библиотека для armeabi-v7a, а с 2022 года правила Google требуют наличие arm64-v8a библиотек.
В-четвертых, структура проекта и его организация были ужасными, это создавало кашу в глазах и мешало нормально ориентироваться в проекте.
И в-пятых, оно было совсем не похоже на Flappy Bird.
В целом попробовал, не получилось, мысль в голове хранилась на протяжении всего этого времени, но попыток больше не предпринималось.

Мотивация

Примерно 14 сентября 2024 года, в дискорд-канале Raylib я увидел как один парень сделал Flappy Bird на языке C#.
Тогда мне стало очень интересно, попробовать давно забытую идею, сделать эту игру на Си, для Android, весом APK меньше 100 Килобайт.
Идея казалась безумной, а также, безуспешной.

Просто представьте, сегодня, когда вес APK достигает по 30-600 Мегабайт, нужно уложиться всего лишь меньше, чем 100 Килобайт.
Для чего такие рамки? Это спортивный интерес, получится ли такое? Получилось! Но было совсем не просто.

Начало пути

Сначала я сделал небольшое решение без участия Visual Studio через батник которое выполняло такие задачи:
1. Компиляция Hello World на Си.
2. Отправка готовой библиотеки в архив APK
3. Упаковка APK
4. Подпись APK

Дальше перешёл на Visual Studio и настраивался там, батник пришлось немного модифицировать, чтобы готовый APK отправлялся мне на телефон для тестирования.

Чем меньше вес, тем лучше

Задача достигнуть цель — APK файл игры по итогу не должен превышать 100 Килобайт.
Нужно было понять, как это можно всё реализовать, чтобы всё работало, при этом на две архитектуры: armeabi-v7a и arm64-v8a. Каждый байт был на счету!

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

Поиск библиотек. Основа для игры

Дальше начались поиски библиотек.
У меня был опыт работы с ImGui, Raylib, Rawdrawandroid, но они все не вписываются в мою цель, потому что по итогу много весят, кроме последнего, там шансы были, но имеется лишний код который мне не нужен, а вырезать всё — заняло бы кучу времени. Поэтому, проще было с нуля всё реализовать.

С графикой определился, это OpenGL ES 2.0. Дальше оставалось решить три проблемы:
1. Шейдеры (никогда не работал с ними, благодаря этому проекту научился)
2. Правильный рендер (posX, posY, width, height на каждый необходимый элемент)
3. Авто-масштабирование элементов (автоматический масштаб под любые экраны и вроде как, это работает хорошо)

Звук

Когда-то я использовал BASS, но он не подходит из-за веса. Слышал о dr_libs, но как оказалось, для моей задачи тоже не подошел, потому что по итогу весит много, да и к тому же, декодировать звук не достаточно, нужно его ещё и воспроизвести. Так я узнал о том, что существует OpenSLES который решает две проблемы сразу: декодирует mp3 и воспроизводит его. Хотя как решение не простой, но осилить можно.

Всё что связано со звуками хранится у проекта здесь

Коротко говоря: воспроизводится звук, создается плеер, после воспроизведения 5 таких плееров предыдущие пересоздаются и в них записывается новый звук.
Плееров может быть и больше, у меня телефон поддерживает до 30 плееров, но в оптимальности я сделал лимит на 5.
*плееры — это звуки которые можно воспроизвести одновременно.
Со звуками очень пришлось повозиться, но результат отличный.

PNG

На моей памяти было только stb_image.h. Но, хоть он и минимальный, но весит также много и не подходит для меня. Поэтому, поисками я наткнулся на такой репозиторий UPNG. Он поддерживает очень мало декодирования в сравнении с stb_image, но на нём удалось всё реализовать (я на это очень надеялся).

Ресурсы для игры взяты частично с оригинала игры, но в большей степени взяты из репозитория Flapper (картинки и звуки). Однако, PNG файлы сразу рендерится не хотели, сыпались артефактами. Поэтому, перетыкав фотошоп, мне удалось подобрать «рабочие» настройки экспорта после которой PNG рендерились как положено.
p.s. я пробовал ещё сжать png различными способами, но получал артефакты в итоге. Иную библиотеку декодера png взять не получится из-за веса, поэтому пришлось остановиться на этом и без сжатия.

Баги

Они есть. К примеру, можно заметить растягивание зеленых столбиков по высоте, как сжимается или расширяется.
А также, кнопки score/share не работают, функционал для них не был реализован, но их нужно было оставить. не пропадать же добру

Распаковка

На разных устройствах, вес установленного приложения был разный, с чем это связано, неизвестно. У меня на на самсунге показывало 150 Кб, а у моего друга 240 Кб.

Полезные ссылки

Мой репозиторий Flappy Bird

Скачать и затестить игру можно здесь

Итоги

Цель достигнута и всё работает!
Оригинал игры весит ~950 Килобайт

p.s. мне не хотелось усложнять статью вставками кода из проекта, поэтому кода в статье нет. В целом было описано, что и как было сделано для достижения результата и с чем сталкивался.
p.s.s. о том как это можно скомпилировать и собрать, есть в readme репозитория


ссылка на оригинал статьи https://habr.com/ru/articles/845068/