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

История

Всё началось в далёком 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.p.s. о том как это можно скомпилировать и собрать, есть в readme репозитория

Комментарии (27)


  1. revellved
    21.09.2024 17:34
    +8

    Flappy Bird для floppy диска


    1. anonymous
      21.09.2024 17:34

      НЛО прилетело и опубликовало эту надпись здесь


  1. astray0b
    21.09.2024 17:34
    +1

    А зачем этому всему Visual Studio с костылями в виде bat-файлов, если в итоге код не на C#? В Android Studio было бы проще.


    1. VadimBoev Автор
      21.09.2024 17:34
      +1

      А зачем мне код на C#, если бы вес был бы явно больше?


      1. astray0b
        21.09.2024 17:34
        +1

        Мой вопрос не в том, почему не на C#, а в том, почему использовалась неподходящая IDE.


        1. VadimBoev Автор
          21.09.2024 17:34

          Хорошо, я вас понял. Android Studio тянет за собой кучу зависимостей в APK, это мне не подходит. К тому же как правило Android Studio используется в основном под java/kotlin, что в моей реализации напрочь отсутствует, а также в ней тупит синтаксис C/C++ и прочее


          1. astray0b
            21.09.2024 17:34
            +9

            Android Studio тянет за собой кучу зависимостей в APK

            Сама студия ничего не тянет. Более того, она и APK сама не собирает, это делает Gradle. А он затянет ровно то, что будет у него в билд-файлах, и не более того.

            в ней тупит синтаксис C/C++

            Что значит «тупит»?


  1. gudvinr
    21.09.2024 17:34
    +5

    Пихать все библиотеки в один APK не надо, есть AAB, а на устройство попадет все равно APK только с библиотеками под нужную архитектуру

    PNG не особо нужен, можно сразу конвертировать в формат, который будет принимать OpenGL - скорее всего ETC1. Тогда сторонние библиотеки не нужны


    1. VadimBoev Автор
      21.09.2024 17:34

      Про AAB знаю, но это относится только к google play как мне известно. Про ETC1 полезно, не вспомнил о нем


      1. gudvinr
        21.09.2024 17:34

        Про AAB знаю, но это относится только к google play как мне известно

        Так и наличие разных библиотек в APK - это тоже требование исключительно для загрузки в Play. На устройство в любом случае попадет только нужный набор библиотек внутри одного APK (если выгружается AAB)

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


  1. antkatcin
    21.09.2024 17:34
    +7

    А когда-то люди вот это https://ru.wikipedia.org/wiki/.kkrieger в 100 килобайт помещали...


    1. Shannon
      21.09.2024 17:34
      +6

      Эти 100кб - это данные для генерации, из которых на лету генерируются тяжелые текстуры, но на "лету" это сильно сказано для ПК тех времен. В памяти это занимает уже 300мб, а требования к процессору и железу выше чем у Far Cry, вышедшей на 2 года раньше: https://habr.com/ru/companies/ruvds/articles/780102/

      Можно такую аналогию провести. Обучить и дистиллировать универсальную Stable Diffusion на какое-то малое подмножество, в итоге модель будет весить допустим 450мб, она будет генерировать текстуры и ресурсы для конкретной игры, а в самой игре будут храниться только текстовые промпты и сиды для генерации. Плюс маленькая нейросеть для апскейлинга этих текстур до 4к. Плюс логика игры допустим еще 50мб.

      В итоге GTA можно упихнуть в 500мб вместо 88гб, но правда запуститься она часов через 5-10 после запуска, и будет иметь требования к процессору и диску в разы выше, чем типичные игры в 500мб. Но на ПК из 2042 это будет занимать секунды и люди будут писать "умели же в 2024 в 500мб запихнуть целую GTA 6".


      1. SadOcean
        21.09.2024 17:34
        +2

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

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


        1. Shannon
          21.09.2024 17:34

          Аналогия не про "припрелать нейросети" ради нейросетей, а про количество мощности необходимой для запуска этих "в 100кб помещали".

          Чтобы эти 100кб запустить и получить симпатичную, но всё-таки посредственную графику, нужен был ПК мощнее, чем необходимый для запуска FarCry на минималках - игры которая была признана одной из самых красивых своего времени, или Doom 3 - игра которая нещадно ставила на колени ПК того времени.

          Аналогия на нейросетях переносит это в современные реалии, которая многим будет понятна более явно, чем "ПК того времени не тянули запуск такой игры в 100кб".
          Можно вместо ресурсов игры хранить только текстовые промпты, игра будет весить "всего 200кб", но запуск потребует пару rtx 4090, процессор на 20 ядер и блок питания на 1000Вт, чтобы это запустилось за 30 минут, а не за 5 часов как на обычном ПК.


    1. 15432
      21.09.2024 17:34
      +12

      Лет через 50 заголовки - "игра всего в 100 гигабайт"


    1. Dmitry_Dor
      21.09.2024 17:34
      +3

      А когда-то люди вот это https://ru.wikipedia.org/wiki/.kkrieger в 100 килобайт помещали...

      На habr была статья

      /nostalgia


  1. cher11
    21.09.2024 17:34
    +2

    А цель была в том числе не использовать Java/Kotlin? Как будто минимальный размер APK можно получить, максимально используя системные библиотеки, а не затаскивая NDK


    1. VadimBoev Автор
      21.09.2024 17:34

      Да, всё верно



  1. firehacker
    21.09.2024 17:34
    +1

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

    Вот я как раз не могу представить, как какой-нибудь вшивый Блокнот или вшивый Список покупок может весить 600 Мб, а игру в 100 Кб мне легко проверить.

    Но статья удивила меня самим фактом возможности писать прикладные приложения под Андроид на Си. Я думал, что только Java. А возможность писать на Си — прямо интригует, хочется вкатиться. Есть квик-гайд?


    1. VadimBoev Автор
      21.09.2024 17:34
      +1

      Когда я изучал эту тему в 21 году, то заметил, что сообщество было очень мало. Прошло 3 года и не заметил особо прироста. В бОльшей степени вся проблема заключается в том, что требуется изучать работу операционки Android в процессе разработки на Си в нативном его представлении. Поэтому, отвечая на ваш вопрос, квик-гайда скорее всего нет, но можно обратить внимание на мой репозиторий, потому что из того что я видел, хотя бы здесь имеется минимально полезный и хорошо читаемый код, с которого можно начать. Если с нуля тяжело, можно попробовать raylib или rawdrawandroid.


      1. astray0b
        21.09.2024 17:34

        Эм, примерно все игры для Android написаны не на джаве. О каком маленьком сообществе вы говорите?


        1. VadimBoev Автор
          21.09.2024 17:34

          О том, кто увлечен созданием приложений на Android на чистом Native Activity без java/kotlin и тому подобное


    1. SadOcean
      21.09.2024 17:34
      +1

      Андроид под капотом ведь линукс.
      Можно писать обычные бинарные библиотеки под эти платформы, есть NDK, есть много примеров к нему.
      Прежде всего он направлен конечно на переиспользование библиотек с других платформ и написание всяких высокоскоростных специализированных библиотек.
      Гугл не рекомендует его использовать, т.к это не платформонезависимо.


  1. goldexer
    21.09.2024 17:34
    +2

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

    Если следом поднимается с пола упавшая челюсть и вопросы «как? а почему же тогда...? получается что...?» - значит, как минимум одной цели автор добился и результат оправдан


  1. xverizex
    21.09.2024 17:34
    +3

    Так как я больше по времени делаю игры, то сделал для себя инструменты. Например Я могу перегнать obj 3d модельки или png картинку в raw формат, который грузиться прямо в opengl. На android есть библиотека zlib и поэтому я с помощью упаковщика ресурсов (своего) сжимаю снова эти файлы и получается маленький размер. У меня есть несколько типов ресурсов, и у каждого свой адрес, например, чтобы загрузить ресурс png текстуры, я пишу.

    Resource *res = resource_manager_get (RES_SPRITES, SCANNER_TEXTURE);

    Да, в android возможно пользоваться готовыми библиотеками для создания игры на opengl es, и в Android Studio это делается удобней. Я тоже на сях писал игру для android, но только вместе с SDL2 для упрощения и скорости разработки.

    А так, вы молодцы. Если будете работать в этом направление, то впереди создание потрясающих инструментов и написания интересных статей.


  1. UniversalSearchBot
    21.09.2024 17:34

    Как же нужна нейросеть которая бы сжимала и оптимизировала код по максимуму. Сама ищет альтернативу и применяет ее в коде. А на выходе весь проект от 30 килобайт до 1 мегабайта. Правда это все грезы.