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

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

177512924cef82efa8f8c593ec3e13eb.gif

История

Всё началось в далёком 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

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

Итоги

Цель достигнута и всё работает!

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

© Habrahabr.ru