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

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

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

Зміст

Частина 1. Мобільний кроссплатформний двигун

Частина 2. Рендеринг UTF-8 тексту за допомогою SDF-шрифту

Частина 3. Рендеринг краплі з прозорістю та відображеннями

А чи потрібен свій движок взагалі?

Щоразу, коли черговий популярний движок стає безкоштовним або відкритим, я ставлю собі це питання. Давайте розглянемо плюси і мінуси:

Плюси

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

Мінуси

  • Все доведеться писати самому, а так само вникати в усі нюанси кожної платформи. Цей процес займе досить багато часу, тому просто так «заради інтересу» цим займатися не варто. Інша справа, якщо ви займаєтеся геймдевом професійно і плануєте випускати досить багато ігор. Практика показує, що більшість ігрових студій рано чи пізно створюють свої SDK.
  • Якщо ви робите проекти на замовлення, то не всі замовники раді вашим самописним рішенням. Адже можливо підтримувати проект доведеться зовсім іншим людям.
  • У багатьох популярних рушіях є вбудовані візуальні 2D/3D редактори. Про їх зручність можна довго сперечатися, але у вас спочатку не буде і цього.

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

З чого це зроблено?

Ми говоримо в першу чергу про розробку мобільних ігор, тому основа буде однозначно на C + +/OpenGL. Без варіантів! Однак без другорядних мов теж не обійтися. Давайте подивимося що використовується на кожній платформі:

Платформа

Основа

Обгортка

Графіка

iOS

C++

ObjectceC або Swift

OpenGL

Android

C++ (NDK)

Java

OpenGL

WindowsPhone

C++

C#

OpenGL через воропер або DirectX

tvOS (AppleTV)

C++

ObjectceC або Swift

OpenGL

OSX

C++

ObjectceC або Swift

OpenGL

Linux

C++

C++

OpenGL

Як бачите C++ і OpenGL зустрічаються всюди. На ObjectceC/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)
          • 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. Самі ж соц. мережі винесені в модулі.
  • Читання/запис файлів.
  • Робота з 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) і шейдерам у грі з шапки.