Как это сделано: мобильный кроссплатформенный движок

  • Tutorial
Для вас подготовил серию статей о мобильном геймдеве, основанную на полученном опыте и пройдённых граблях. В первой статье речь пойдёт о создании собственного кроссплатформенного движка для мобильных игр. По правде говоря не только мобильных, и не только игр.


Содержание


Часть 1. Мобильный кроссплатформенный движок
Часть 2. Рендеринг UTF-8 текста с помощью SDF шрифта
Часть 3. Рендеринг капли с прозрачностью и отражениями


А нужен ли свой движок вообще?


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

Плюсы

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

Минусы

  • Все придётся писать самому, а так же вникать во все нюансы каждой платформы. Этот процесс займёт довольно много времени, поэтому просто так «ради интереса» этим заниматься не стоит. Другое дело, если вы занимаетесь геймдевом профессионально и планируете выпускать довольно много игр. Практика показывает, что большинство игровых студий рано или поздно создают свои SDK.
  • Если вы делаете проекты на заказ, то не все заказчики рады вашим самописным решениям. Ведь возможно поддерживать проект придется совершенно другим людям.
  • Во многих популярных движках есть встроенные визуальные 2D/3D редакторы. Об их удобстве можно долго спорить, но у вас изначально не будет и этого.

Конечно каждый для себя увидит свои плюсы и минусы. Мое дело предупредить. Поехали!

Из чего это сделано?


Мы говорим в первую очередь о разработке мобильных игр, поэтому основа будет однозначно на C++/OpenGL. Без вариантов! Однако без второстепенных языков тоже не обойтись. Давайте посмотрим что используется на каждой платформе:
Платформа Основа Обертка Графика
iOS C++ ObjectiveC или Swift OpenGL
Android C++ (NDK) Java OpenGL
WindowsPhone C++ C# OpenGL через врапер или DirectX
tvOS (AppleTV) C++ ObjectiveC или Swift OpenGL
OSX C++ ObjectiveC или Swift OpenGL
Linux C++ C++ OpenGL
Как видите C++ и OpenGL встречаются везде. На ObjectiveC/Java/C# придется написать только обертку для работы с системой девайса. Сам же код ваших проектов будет единый — на С++. На этой ноте скажем: «До свидания, мучительное портирование!».

OpenGL


Настоятельно рекомендую использовать OpenGL 2.0 и выше. Время OpenGL 1.1 давно прошло, а переход с 1.х на 2.х вы будете вспоминать в кошмарных снах. Однако не спешите использовать последнюю версию OpenGL не убедившись, что все целевые платформы его поддерживают. В большинстве случаев OpenGL 2.0 вполне хватает и поддерживают его все платформы.

С++


Та же ситуация и с С++11/14. Если уверены, что все компиляторы с ним дружат – супер. Мне же хватает C++98, так что при добавлении новой платформы — а в планах есть поддержка консолей — я буду спокоен.

IDE


Xcode – для iOS, OSX, tvOS. Плагины через CocoaPods.
Android Studio – для Android. Плагины через Gradle.
Visual Studio – все что под Windows.

Структура движка


Прежде всего движок и проекты должны аккуратно и логично храниться на диске. В итоге я пришел к такой структуре:

  • Engine (все что касается движка)
    • Classes (.h, .cpp файлы движка)
    • Modules (модули и сторонние SDK, которые нужны не во всех проектах)
      • Рекламные SDK
      • Аналитика
      • Game Center
      • Изображения
      • Социальные сетки
      • … и т.д.
    • Platforms (специфические классы по платформам)
      • Android
      • iOS
      • OSX
      • tvOS
      • … прочие платформы

  • Тестовый проект
    • iOS
      • Проект.xcworkspace
      • Icons (иконки приложения, *auto — сборщик проекта сам заполняет эти папки)
      • Launch (картинки при старте приложения, *auto)
      • Res (готовые ресурсы приложения, *auto)
      • Pods
      • … прочие файлы ios проекта, plist, build и т.д.
    • Android, OSX, tvOS… такие же по смыслу папки под разные платформы и IDE. Для Android Studio своя структура проекта.
    • Assets
      • Icons (иконки приложения всех размеров)
      • Launch (ланч-скрины всех размеров)
    • Resources (оригиналы ресурсов проекта)
      • General (основные ресурсы для всех платформ)
      • Lang (шрифты и локализация)
        • Fonts (папка с SDF шрифтами)
        • Lang.xls (файл с переводами)
      • Platform (ресурсы специфические для платформы)
        • iOS
        • Android
        • … прочие платформы
      • Shaders (шейдеры)
      • Sounds (звуки)
        • MP3 (для треков)
        • OGG (для звуков)
      • Textures (ресурсы по форматам текстур)
        • ATI
        • ETC
        • PVRTC
        • S3TC
    • Source (.h, .cpp файлы самого проекта)
    • Config (файл настроек проекта для сборщика)

Сборщик проекта


Сборщик проекта отвечает за подготовку ресурсов, форматы и упаковку. А именно:
  • Берет иконки (или даже одну иконку максимального размера 1024х1024) из Assets/Icons, делает остальные размеры (от 16х16 до 1024х1024) и копирует в папки по платформам [Platform]/Icons
  • Так же поступает с экранами старта из Assets/Launch
  • Берет ресурсы приложения из следующих папок:
    • Resources/General
    • Resources/Shaders
    • Resources/Sounds/MP3, OGG
    • Resources/Textures/[нужный формат текстур]
    • Resources/Platform/[платформа]
    • Resources/Lang/Fonts
Далее сборщик конвертирует ресурсы, шифрует, упаковывает и помещает в [Platform]/Res.

Самое важное тут – это конвертация файлов по расширению. Я использую такие конвертации:
  • PNG и JPEG превращаются в WEBP. Общие настройки конвертации (например минимальное качество) можно вынести в настроки проекта Project/Config, а можно и указать прямо в названии файла.
    Например image~q100.png будет сжата с параметром quality 100, а image~less.png будет сжата без потери качества.
    Так же хорошо себя зарекомендовали пресеты.
    Например к image~p1.png будет применен 1й пресет, который перевернет картинку зеркально и сохранит с качеством 90%.
  • Текстовые файлы и шейдеры (.txt, .vs, .ps) шифруются нехитрым способом. Простая защита от любопытных.
  • Файл локализации Resources/Lang/Lang.xls парсится по языкам, шифруется и упаковывается в бинарный формат.
  • На лету создаются текстурные атласы. К примеру из папки с именем folder~atlas будут взяты все картинки и упакованы в единую картинку + сохранится файлик с координатами.
  • Звуки конвертируются в OGG формат.
  • 3D модели конвертируются во внутренний формат движка.
  • Файлы с именем file.pack преобразуются из текстовых в бинарные. Это хорошо подходит для всевозможных конфигов игры, уровней и т.д.

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

Форматы


Я бы рекомендовал использовать такие форматы:

WEBP для картинок. Вряд ли для кого-нибудь этот формат окажется новым. А для тех, кто слышит о нем впервые – webp может хранить картинку без потери качества как PNG, а так же с потерей — как JPEG, однако с заметно лучшим качеством, меньшем весом и с прозрачностью. Еще из плюсов – возможность скейла картинки на лету при чтении файла. Компилируется libwebp под все платформы без проблем.

OGG для звуков. Андроид нативно понимает OGG формат, а на iOS/OSX/tvOS я использую библиотеку Tremor (fixed-point version of the Ogg Vorbis) для раскодировки звуков в WAV и скармливанию их OpenAL. Попытки использовать OpenAL и на андроиде успехом не увенчались (звуки были с задержками).

Классы и модули


Разберем подробнее какие классы содержит движок и для чего нужны модули?
Правило «что выносить в модуль, а что в движок?» очень простое:


Следуя этому правилу, я распределил классы следующим образом:

Движок


  • Работа с платформой. Тут происходит инициализация приложения, а так же передача внешних событий (пауза, тачскрин, кнопки) в основной класс движка.
  • Основной класс. Тут крутится mainloop, обрабатываются входящие события (уже в универсальном виде независимом от платформы), происходит управление потокам и фоновыми задачами.
  • Работа с 2D. Вывод картинок, атласов, постэффектов.
  • Работа с 3D моделями. Загрузка моделей, рендеринг, управление шейдерами.
  • Стандартные UI элементы. Окна, кнопки, скролинг, уведомления.
  • Текстуры. Загрузка и выгрузка текстур. Сами декодеры находятся в модулях.
  • Вывод текста, рендеринг SDF шрифтов.
  • Математика. Всевозможные формулы, матрицы, кватернионы и т.д.
  • Социалка. Отправка писем, стандартный шаринг, rate me. Сами же соц. cети вынесены в модули.
  • Чтение/запись файлов.
  • Работа с UTF8 строками.
  • Работа с сетью.
  • Музыка/звуки.

Модули


  • Социалка
    • Facebook
    • Google Plus
    • Twitter
    • VK
  • Replay Kit (запись экрана для iOS)
  • JSON/XML
  • Декодеры картинок
    • WEBP
    • JPEG
    • PNG
  • In-apps (внутренние платежи)
  • Game Center, Google Play Services
  • Crashlytics (отслеживать краши)
  • Branch (глубокие ссылки)
  • Аналитика
    • Google Analytics
    • Game Analytics
    • Flurry
  • Реклама
    • Appodeal (UPD недобросовестная компания)
    • Chartboost
    • Fyber
    • AdColony
    • UnityAds
    • Tapjoy
    • Google Ads
    • Heyzap
    • AdToApp

В следующих статьях я подробнее остановлюсь на конкретных классах и модулях, с примерами и полезностями. Отдельное внимание хочу уделить рендерингу SDF шрифтов (Signed Distance Field) и шейдерам в игре из шапки.
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама
Комментарии 49
  • 0
    Исходники покажете?
    Ну и без C++11 писать это боль все-таки.

    Долго весь движок писали? Один?
    • 0
      Целиком исходники не покажу, но отдельные модули буду скидывать в след. статьях. Например модуль ReplayKit и модуль для захвата видео игры под OSX (при чем 1080p 60fps, что помогло в создании промо-видео к игре), выложу полностью. Да, писал один. Последняя глобальная переделка движка была в 2014г. В посте ниже подробнее описал историю создания движка.
    • 0
      Сначала прочитал статью по диагонали, думаю, а где название фреймворка, не уж то наш Marmalade SDK упомянули в кое то веки. Почти все есть из выше написанного. Если сделали сами — врете, нерельно, команда — возможно, но откуда финансирование? Если первое — то откуда опен-сорс базу взяли и потом на ней писали? И т.д.
      • 0
        О Мармеладе слышал много хорошего (с ним много работает дружественный мне издатель), но сам с ним не работал. Делал все действительно сам, и это не мудрено, если занимаешься мобайлом с 2008 года и за плечами порты игры Race illegal: High Speed 3D на J2Me, iOS, OSX, Android, WP8, Tizen и недавно еще tvOS добавился, правда для другой игры (та что в шапке и о которой будет отдельная статья). Движок родился в процессе разработки, был несколько раз переделан с нуля, а финансирование — мои же игры, на этом же движке.
        • +3

          Так а движок опен-сорс? А то толку от статьи пока никакой и попахивает самопиаром ;-)

          • +1
            Движок закрытый. Это первая часть серии статей. Следующие статьи будут разные, не только о движке. Например про шейдер катающейся капли напишу, а так же про левел-билдер на WebGL. И вообще буду рад написать обо всем, что люди попросят описать подробнее в каментах. Так что это не пиар, надо куда-то слить знания, а то голова пухнет :)
      • 0
        Однозначно плюс в карму. Но зачем заниматься вилосипедостроительством в 2016 году…
        • 0
          Согласен, сейчас многие достойные движки стали доступны инди-разработчикам. Свой движок — это не простой путь, однако он весьма… познавательный.
          • 0
            Как раз наткнулся на вторую статью про шрифты из sdf… оказывается я шаг за шагом повторяю ваш путь, коллега :)
        • 0
          Вопрос. Легко ли можно писать под любой IDE? Ну т.е. у меня винда к примеру, девайс WP… у коллеги iOs и xcode, а у другого адроид. Мы легко можем вместе работать над одной игрой? Или основная разработка ведется только в одной оси и в одной IDE?
          • +1

            Можно заюзать cmake для генерации прожект файлов для любой IDE. В итоге можно будет пилить одну кодобазу с разных ОС

            • 0
              Да, вполне легко. Собственно тестируя игру под разные платформы, вы в любом случае запускаете разные IDE и собираете проект там. При этом сам код и ресурсы игры единые. Ну почти единые, конечно для телефона и телевизора будут отличия и в управлении, и в UI. Но эти отличия спокойно помещаются в одном .cpp файле.
              Лично я 80% времени тестирую и пишу игру под OSX (именно запускаю собранную игру), по простой причине — скорость компиляции и запуска. Когда запускаешь игру по 20 раз в час, то скорость запуска сильно бережет нервы.
              К тому же под OSX могу менять размер окна и сразу видеть как игра смотрится на телефонах и планшетах разных размеров, ура резиновым интерфейсам. На самих девайсах же запускаю более детальные тесты, например отладить чувствительность управления гироскопом и т.д.
              • 0

                Личнчо мне интересно было бы почитать про рендереры на вулкане+металле :-) 2016ый год же, как тут заметили в комментариях.

            • 0
              Почему адмоб не значится в списке рекламных модулей? :)
            • 0
              OpenGL 2.0? В 2016? Вы серьёзно?
              В своём движке на D я использую OpenGL 4.5. На мобильные платформы ещё не портировал, но OpenGL ES 3.0 более-менее совместим.
              Но пока это малоприоритетно, т.к. основная аудитория — пользователи ПК.
              • 0
                ES 3.0 процентов у 60 юзеров будет.

                И игра ваша не запустится у большинства десктопных юзеров. GL 4.5 это GTX400 и выше. Успехов с чем нибудь вроде Intel HD

                Ну а вообще, выпендриваться что вы пишете на языке с сырой экосистемой под недавно появившийся стандарт — удел школьников, которые как правило ничего не довели до конца в своей жизни. Тут статья о реальности.
                • –2
                  Есть замечательная штука — steam hwsuvey. Пользователей со старыми видеокартами очень мало. К примеру HD5000 от AMD поддерживает 4.5 и вышло в 2009 году. Я думаю, за 7 лет можно обновить видеокарту.
                  С картой старее — в любую более-менее современную игрушку играть невозможно.

                  Что касается встроек — они как бы не для игр. У Intel поддержка OpenGL всегда была никчёмной.
                  Ах да, игра выходит в мае в бету. Движок полноценно поддерживает тени, освещение, постпроцессинг, геометрические и тесселяционные шейдеры. Напишу статью :)

                  Реальность — это о том, что нужно писать на стандарте, который вышел 15 лет назад? Серьёзно, 2.0 вышло в 2001 году.
                  • +2
                    Ок, смотрим Apple Hardware GPU Information. Например iPad 2 не поддерживает OpenGL 3.0 и далеко не во всех играх нужна тесселяция. Поднимать версию без веской причины не вижу смысла.
                    • 0
                      Вы распространяете игру только через Steam? Если нет выборка не репрезентативна.
                      • 0
                        HD5000 поддерживает 4.4,

                        4.5 — это HD7000, это ~4 года.
                        • 0
                          Кримсон поддерживает 4.5 для всех карт, начиная с 5к.
                    • +1
                      Мой старый Android худо-бедно 2.0 поддерживает, а вы про 4.5…
                      • 0
                        В андроиде используется OpenGL ES. Вторая его версия аналогична десктопному 3.Х.
                        По статистике Unity он поддерживается на 98% телефонов.
                        Мне кажется, это был бы правильный выбор.
                        • 0
                          Судя по вики, он поддерживается всегда с Android 2.2+.
                          Я сомневаюсь, что игрушки автора на 2.1 будут работать.
                    • 0
                      Извините конечно, а у вас точно движок? Почему основной части уделена одна строчка Classes (.h, .cpp файлы движка)?
                      А как же такие глобальные вещи как рендер, физика и логика? Мне хочется понять, что именно вы написали помимо поддержки разных платформ и социалки. У меня к вам по каждому пункту куча вопросов. Давайте возьмем самое простое «Чтение/запись файлов.». Стриминг у вас есть?
                      • 0
                        Вы абсолютно правы, именно в Classes находятся самые главные файлы. В одну статью все не поместилось, буду конкретизировать в продолжениях.
                        Например для симуляции использую физику верле. Рендер оптимизирует смену стейтов, занимается камерой/проекциями, группами, подбором нужного шейдера и его настройкой.
                        «Чтение/запись файлов» упрощает работу с fopen, fread, fwrite и т.д. Из полезного — расшифровывает файлы, понимает по каким путям где что лежит, читает ресурсы непосредственно из apk. Стриминг (fstream) не использую, как-то не нужен нигде был.
                        • 0
                          Ну тогда на правах коллеги позвольте вас попытать для обмена опытом ) По рендеру, у вас есть система частиц, лоды, террейн рендеринг, порталы, костевая анимация, деколи? Столько вопросов по реализации )
                          • 0
                            Буду рад, пытайте :) Частицы использовались в нескольких проектах, а значит пора их выносить в модуль. Лоды, скелетная анимация и горы тоже были, но только в одном проекте, поэтому пока выносить их в движок не спешу. Стоит уточнить, что у меня нет задачи создать универсальный движок. Появляются новые проекты и моя цель максимально ускорить процесс создания игры с минимальным портированием.
                        • +1
                          Пожалуй mainloop — это самое важное, что есть в основном классе. Именно там происходит управление fps, обработка тачей/кнопок, выполнение тасков в основном потоке (особенно для OpenGL без shared context актуально и изменений UI из фоновых потоков), поддержка слоев (аля окна), смена стейтов приложения (меню/уровень/магазин/.../лоадинг скрин). Видимо это еще одна тема для статьи :)
                          • 0
                            Обязательно напишите, т.к. тема очень интересная )
                        • 0
                          А зачем писать именно с нуля? Есть так хочется своих кастомных вещей в движке, то почему бы не взять открытый существующий и просто менять/расширять его как душе вздумается?
                          • 0
                            Какой, например, движок на С++ можно взять? :)
                          • +1
                            Где можно посмотреть на игры сделанные на этом движке? А то может там тетрис или flappybird :)
                            • 0
                              Можете посмотреть сайт, указанный в моем профайле.
                              • 0
                                Нет там ничего, высокий уровень приватности наверно
                                • 0
                                  Сори, поменял уровень приватности. Пожалуйста, смотрите.
                            • –1
                              Такой вопрос, кстати. Известно, что в играх порой очень сложно обойтись без скриптов, увеличивающих гибкость движка. И как с этим на iOS, где любые интерпретаторы запрещены? lua, так понимаю, не используешь?
                              • 0
                                Особой необходимости в скриптах пока не было. Вернее, уровни созданные в левел-билдере на webGL конвертировались в логику на С++. Но это была внутренняя логика игры и в движок ее не выносил. Если понадобятся в будущем скрипты, то буду использовать Lua. В AppStore вроде использовать Lua не запрещено.
                                • 0
                                  А если я, к примеру, получаю от сервера команду на установку определённого уровня пользователю, будет ли такой трюк считаться нарушением? К примеру, у меня на сервере логируется игра пользователя и бекапится текущий прогресс. Если пользователь случайно потерял прогресс, я могу восстановить его с сервера. Как они к такому отнесутся?
                                  • 0

                                    Такое вполне допустимо и встречается почти во всех SDK. Например:


                                    • save/load прогресса на своих серверах
                                    • сегментация игроков для кастомизации игры (покупающим игрокам показываем один магазин, остальным — другой)
                                    • проверка in-apps на сервере (при обнаружении взлома имеет смысл обнулить прогресс игрока)
                                    • A/B тестирование
                                    • загрузка/обновление конфигов/уровней

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

                                    • 0
                                      Да собственно, и интерпретатор lua не запрещен. Запрещены jit компиляторы, причем на уровне sdk, у вас просто не получится их запустить. К примеру luajit будет работать в режиме интерпретатора (хотя все равно в 5 раз быстрее чем оригинальный lua интерпретатор)
                                  • +2

                                    Если вы будете скачивать скрипты на Lua из интернетов и запускать — тогда эпл имеет право вас забанить (есть в апп сторе такой пункт), а так — на здоровье.

                                    • +1
                                      ЕМНИП запрещено интерпретировать код, загруженный в обход аппстора. Если интерпретируемый код содержится в бандле приложения, то проблем быть не должно.
                                    • –1
                                      так или иначе — это все равно велосипед от гиков и для гиков,
                                      • 0
                                        А почему об Appodeal возникло такое мнение, что это недобросовестная компания?
                                        • 0

                                          Они пиарятся за счет конкурсов, в условия которых прячут уловки. Лично мое отношение к ним это подорвало.

                                          • 0
                                             Так а доходность реально позволяют выше получать, чем от простого адмоба или нет?
                                            • 0

                                              У меня пока нет полной статистики, чтобы сравнить с чистым адмобом. Могу сравнить между агрегаторами: Fyber, Appodeal, Heyzap, Adtoapp. Последние два оказались совсем хиленькие. А Fyber и Appodeal примерно на равне.

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