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


Содержание


Часть 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
    • Chartboost
    • Fyber
    • AdColony
    • UnityAds
    • Tapjoy
    • Google Ads
    • Heyzap
    • AdToApp

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

Если какие-то отдельные вопросы вас заинтересовали – плз пишите в каментах, добавлю их в план статей.

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


  1. stepanp
    20.04.2016 20:06

    Исходники покажете?
    Ну и без C++11 писать это боль все-таки.

    Долго весь движок писали? Один?


    1. Apetrus
      20.04.2016 21:36

      Целиком исходники не покажу, но отдельные модули буду скидывать в след. статьях. Например модуль ReplayKit и модуль для захвата видео игры под OSX (при чем 1080p 60fps, что помогло в создании промо-видео к игре), выложу полностью. Да, писал один. Последняя глобальная переделка движка была в 2014г. В посте ниже подробнее описал историю создания движка.


  1. maksqwe
    20.04.2016 20:59

    Сначала прочитал статью по диагонали, думаю, а где название фреймворка, не уж то наш Marmalade SDK упомянули в кое то веки. Почти все есть из выше написанного. Если сделали сами — врете, нерельно, команда — возможно, но откуда финансирование? Если первое — то откуда опен-сорс базу взяли и потом на ней писали? И т.д.


    1. Apetrus
      20.04.2016 21:33

      О Мармеладе слышал много хорошего (с ним много работает дружественный мне издатель), но сам с ним не работал. Делал все действительно сам, и это не мудрено, если занимаешься мобайлом с 2008 года и за плечами порты игры Race illegal: High Speed 3D на J2Me, iOS, OSX, Android, WP8, Tizen и недавно еще tvOS добавился, правда для другой игры (та что в шапке и о которой будет отдельная статья). Движок родился в процессе разработки, был несколько раз переделан с нуля, а финансирование — мои же игры, на этом же движке.


      1. Nagg
        20.04.2016 21:35
        +3

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


        1. Apetrus
          20.04.2016 21:46
          +1

          Движок закрытый. Это первая часть серии статей. Следующие статьи будут разные, не только о движке. Например про шейдер катающейся капли напишу, а так же про левел-билдер на WebGL. И вообще буду рад написать обо всем, что люди попросят описать подробнее в каментах. Так что это не пиар, надо куда-то слить знания, а то голова пухнет :)


  1. Konstontin
    20.04.2016 21:18

    Однозначно плюс в карму. Но зачем заниматься вилосипедостроительством в 2016 году…


    1. Apetrus
      20.04.2016 22:01

      Согласен, сейчас многие достойные движки стали доступны инди-разработчикам. Свой движок — это не простой путь, однако он весьма… познавательный.


      1. Shifty_Fox
        21.04.2016 22:54

        Как раз наткнулся на вторую статью про шрифты из sdf… оказывается я шаг за шагом повторяю ваш путь, коллега :)


  1. midday
    20.04.2016 21:24

    Вопрос. Легко ли можно писать под любой IDE? Ну т.е. у меня винда к примеру, девайс WP… у коллеги iOs и xcode, а у другого адроид. Мы легко можем вместе работать над одной игрой? Или основная разработка ведется только в одной оси и в одной IDE?


    1. Nagg
      20.04.2016 21:28
      +1

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


    1. Apetrus
      20.04.2016 22:16

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


      1. Nagg
        21.04.2016 20:32

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


  1. SEOVirus
    20.04.2016 23:03

    Почему адмоб не значится в списке рекламных модулей? :)


    1. DnV
      21.04.2016 00:02

      Google Ads же


  1. Temtaime
    20.04.2016 23:33

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


    1. GrigoryPerepechko
      21.04.2016 01:08

      ES 3.0 процентов у 60 юзеров будет.

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

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


      1. Temtaime
        21.04.2016 01:38
        -2

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

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

        Реальность — это о том, что нужно писать на стандарте, который вышел 15 лет назад? Серьёзно, 2.0 вышло в 2001 году.


        1. Apetrus
          21.04.2016 01:52
          +2

          Ок, смотрим Apple Hardware GPU Information. Например iPad 2 не поддерживает OpenGL 3.0 и далеко не во всех играх нужна тесселяция. Поднимать версию без веской причины не вижу смысла.


        1. Sirikid
          21.04.2016 02:07

          Вы распространяете игру только через Steam? Если нет выборка не репрезентативна.


    1. Suvitruf
      21.04.2016 10:24
      +1

      Мой старый Android худо-бедно 2.0 поддерживает, а вы про 4.5…


      1. Temtaime
        21.04.2016 14:11

        В андроиде используется OpenGL ES. Вторая его версия аналогична десктопному 3.Х.
        По статистике Unity он поддерживается на 98% телефонов.
        Мне кажется, это был бы правильный выбор.


        1. Temtaime
          21.04.2016 14:17

          Судя по вики, он поддерживается всегда с Android 2.2+.
          Я сомневаюсь, что игрушки автора на 2.1 будут работать.


  1. KyHTEP
    21.04.2016 01:04

    Извините конечно, а у вас точно движок? Почему основной части уделена одна строчка Classes (.h, .cpp файлы движка)?
    А как же такие глобальные вещи как рендер, физика и логика? Мне хочется понять, что именно вы написали помимо поддержки разных платформ и социалки. У меня к вам по каждому пункту куча вопросов. Давайте возьмем самое простое «Чтение/запись файлов.». Стриминг у вас есть?


    1. Apetrus
      21.04.2016 01:31

      Вы абсолютно правы, именно в Classes находятся самые главные файлы. В одну статью все не поместилось, буду конкретизировать в продолжениях.
      Например для симуляции использую физику верле. Рендер оптимизирует смену стейтов, занимается камерой/проекциями, группами, подбором нужного шейдера и его настройкой.
      «Чтение/запись файлов» упрощает работу с fopen, fread, fwrite и т.д. Из полезного — расшифровывает файлы, понимает по каким путям где что лежит, читает ресурсы непосредственно из apk. Стриминг (fstream) не использую, как-то не нужен нигде был.


      1. KyHTEP
        21.04.2016 01:49

        Ну тогда на правах коллеги позвольте вас попытать для обмена опытом ) По рендеру, у вас есть система частиц, лоды, террейн рендеринг, порталы, костевая анимация, деколи? Столько вопросов по реализации )


        1. Apetrus
          21.04.2016 02:05

          Буду рад, пытайте :) Частицы использовались в нескольких проектах, а значит пора их выносить в модуль. Лоды, скелетная анимация и горы тоже были, но только в одном проекте, поэтому пока выносить их в движок не спешу. Стоит уточнить, что у меня нет задачи создать универсальный движок. Появляются новые проекты и моя цель максимально ускорить процесс создания игры с минимальным портированием.


    1. Apetrus
      21.04.2016 01:42
      +1

      Пожалуй mainloop — это самое важное, что есть в основном классе. Именно там происходит управление fps, обработка тачей/кнопок, выполнение тасков в основном потоке (особенно для OpenGL без shared context актуально и изменений UI из фоновых потоков), поддержка слоев (аля окна), смена стейтов приложения (меню/уровень/магазин/.../лоадинг скрин). Видимо это еще одна тема для статьи :)


      1. KyHTEP
        21.04.2016 15:40

        Обязательно напишите, т.к. тема очень интересная )


  1. agent10
    21.04.2016 08:42

    А зачем писать именно с нуля? Есть так хочется своих кастомных вещей в движке, то почему бы не взять открытый существующий и просто менять/расширять его как душе вздумается?


  1. ANTPro
    21.04.2016 09:22
    +1

    Где можно посмотреть на игры сделанные на этом движке? А то может там тетрис или flappybird :)


    1. Apetrus
      21.04.2016 10:41

      Можете посмотреть сайт, указанный в моем профайле.


      1. ANTPro
        21.04.2016 10:44

        Нет там ничего, высокий уровень приватности наверно


        1. Apetrus
          21.04.2016 10:51

          Сори, поменял уровень приватности. Пожалуйста, смотрите.


  1. pda0
    21.04.2016 18:34
    -1

    Такой вопрос, кстати. Известно, что в играх порой очень сложно обойтись без скриптов, увеличивающих гибкость движка. И как с этим на iOS, где любые интерпретаторы запрещены? lua, так понимаю, не используешь?


    1. Apetrus
      21.04.2016 18:39

      Особой необходимости в скриптах пока не было. Вернее, уровни созданные в левел-билдере на webGL конвертировались в логику на С++. Но это была внутренняя логика игры и в движок ее не выносил. Если понадобятся в будущем скрипты, то буду использовать Lua. В AppStore вроде использовать Lua не запрещено.


    1. Nagg
      21.04.2016 20:28
      +2

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


    1. mkarev
      21.04.2016 20:39
      +1

      ЕМНИП запрещено интерпретировать код, загруженный в обход аппстора. Если интерпретируемый код содержится в бандле приложения, то проблем быть не должно.


  1. demonit
    26.04.2016 09:04
    -1

    так или иначе — это все равно велосипед от гиков и для гиков,