Содержание
Часть 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 |
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 (файл настроек проекта для сборщика)
- iOS
Сборщик проекта
Сборщик проекта отвечает за подготовку ресурсов, форматы и упаковку. А именно:
- Берет иконки (или даже одну иконку максимального размера 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
Самое важное тут – это конвертация файлов по расширению. Я использую такие конвертации:
- 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 строками.
- Работа с сетью.
- Музыка/звуки.
Модули
- Социалка
- Google Plus
- 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)
maksqwe
20.04.2016 20:59Сначала прочитал статью по диагонали, думаю, а где название фреймворка, не уж то наш Marmalade SDK упомянули в кое то веки. Почти все есть из выше написанного. Если сделали сами — врете, нерельно, команда — возможно, но откуда финансирование? Если первое — то откуда опен-сорс базу взяли и потом на ней писали? И т.д.
Apetrus
20.04.2016 21:33О Мармеладе слышал много хорошего (с ним много работает дружественный мне издатель), но сам с ним не работал. Делал все действительно сам, и это не мудрено, если занимаешься мобайлом с 2008 года и за плечами порты игры Race illegal: High Speed 3D на J2Me, iOS, OSX, Android, WP8, Tizen и недавно еще tvOS добавился, правда для другой игры (та что в шапке и о которой будет отдельная статья). Движок родился в процессе разработки, был несколько раз переделан с нуля, а финансирование — мои же игры, на этом же движке.
Nagg
20.04.2016 21:35+3Так а движок опен-сорс? А то толку от статьи пока никакой и попахивает самопиаром ;-)
Apetrus
20.04.2016 21:46+1Движок закрытый. Это первая часть серии статей. Следующие статьи будут разные, не только о движке. Например про шейдер катающейся капли напишу, а так же про левел-билдер на WebGL. И вообще буду рад написать обо всем, что люди попросят описать подробнее в каментах. Так что это не пиар, надо куда-то слить знания, а то голова пухнет :)
Konstontin
20.04.2016 21:18Однозначно плюс в карму. Но зачем заниматься вилосипедостроительством в 2016 году…
Apetrus
20.04.2016 22:01Согласен, сейчас многие достойные движки стали доступны инди-разработчикам. Свой движок — это не простой путь, однако он весьма… познавательный.
Shifty_Fox
21.04.2016 22:54Как раз наткнулся на вторую статью про шрифты из sdf… оказывается я шаг за шагом повторяю ваш путь, коллега :)
midday
20.04.2016 21:24Вопрос. Легко ли можно писать под любой IDE? Ну т.е. у меня винда к примеру, девайс WP… у коллеги iOs и xcode, а у другого адроид. Мы легко можем вместе работать над одной игрой? Или основная разработка ведется только в одной оси и в одной IDE?
Nagg
20.04.2016 21:28+1Можно заюзать cmake для генерации прожект файлов для любой IDE. В итоге можно будет пилить одну кодобазу с разных ОС
Apetrus
20.04.2016 22:16Да, вполне легко. Собственно тестируя игру под разные платформы, вы в любом случае запускаете разные IDE и собираете проект там. При этом сам код и ресурсы игры единые. Ну почти единые, конечно для телефона и телевизора будут отличия и в управлении, и в UI. Но эти отличия спокойно помещаются в одном .cpp файле.
Лично я 80% времени тестирую и пишу игру под OSX (именно запускаю собранную игру), по простой причине — скорость компиляции и запуска. Когда запускаешь игру по 20 раз в час, то скорость запуска сильно бережет нервы.
К тому же под OSX могу менять размер окна и сразу видеть как игра смотрится на телефонах и планшетах разных размеров, ура резиновым интерфейсам. На самих девайсах же запускаю более детальные тесты, например отладить чувствительность управления гироскопом и т.д.Nagg
21.04.2016 20:32Личнчо мне интересно было бы почитать про рендереры на вулкане+металле :-) 2016ый год же, как тут заметили в комментариях.
Temtaime
20.04.2016 23:33OpenGL 2.0? В 2016? Вы серьёзно?
В своём движке на D я использую OpenGL 4.5. На мобильные платформы ещё не портировал, но OpenGL ES 3.0 более-менее совместим.
Но пока это малоприоритетно, т.к. основная аудитория — пользователи ПК.GrigoryPerepechko
21.04.2016 01:08ES 3.0 процентов у 60 юзеров будет.
И игра ваша не запустится у большинства десктопных юзеров. GL 4.5 это GTX400 и выше. Успехов с чем нибудь вроде Intel HD
Ну а вообще, выпендриваться что вы пишете на языке с сырой экосистемой под недавно появившийся стандарт — удел школьников, которые как правило ничего не довели до конца в своей жизни. Тут статья о реальности.Temtaime
21.04.2016 01:38-2Есть замечательная штука — steam hwsuvey. Пользователей со старыми видеокартами очень мало. К примеру HD5000 от AMD поддерживает 4.5 и вышло в 2009 году. Я думаю, за 7 лет можно обновить видеокарту.
С картой старее — в любую более-менее современную игрушку играть невозможно.
Что касается встроек — они как бы не для игр. У Intel поддержка OpenGL всегда была никчёмной.
Ах да, игра выходит в мае в бету. Движок полноценно поддерживает тени, освещение, постпроцессинг, геометрические и тесселяционные шейдеры. Напишу статью :)
Реальность — это о том, что нужно писать на стандарте, который вышел 15 лет назад? Серьёзно, 2.0 вышло в 2001 году.Apetrus
21.04.2016 01:52+2Ок, смотрим Apple Hardware GPU Information. Например iPad 2 не поддерживает OpenGL 3.0 и далеко не во всех играх нужна тесселяция. Поднимать версию без веской причины не вижу смысла.
Sirikid
21.04.2016 02:07Вы распространяете игру только через Steam? Если нет выборка не репрезентативна.
Suvitruf
21.04.2016 10:24+1Мой старый Android худо-бедно 2.0 поддерживает, а вы про 4.5…
Temtaime
21.04.2016 14:11В андроиде используется OpenGL ES. Вторая его версия аналогична десктопному 3.Х.
По статистике Unity он поддерживается на 98% телефонов.
Мне кажется, это был бы правильный выбор.Temtaime
21.04.2016 14:17Судя по вики, он поддерживается всегда с Android 2.2+.
Я сомневаюсь, что игрушки автора на 2.1 будут работать.
KyHTEP
21.04.2016 01:04Извините конечно, а у вас точно движок? Почему основной части уделена одна строчка Classes (.h, .cpp файлы движка)?
А как же такие глобальные вещи как рендер, физика и логика? Мне хочется понять, что именно вы написали помимо поддержки разных платформ и социалки. У меня к вам по каждому пункту куча вопросов. Давайте возьмем самое простое «Чтение/запись файлов.». Стриминг у вас есть?Apetrus
21.04.2016 01:31Вы абсолютно правы, именно в Classes находятся самые главные файлы. В одну статью все не поместилось, буду конкретизировать в продолжениях.
Например для симуляции использую физику верле. Рендер оптимизирует смену стейтов, занимается камерой/проекциями, группами, подбором нужного шейдера и его настройкой.
«Чтение/запись файлов» упрощает работу с fopen, fread, fwrite и т.д. Из полезного — расшифровывает файлы, понимает по каким путям где что лежит, читает ресурсы непосредственно из apk. Стриминг (fstream) не использую, как-то не нужен нигде был.KyHTEP
21.04.2016 01:49Ну тогда на правах коллеги позвольте вас попытать для обмена опытом ) По рендеру, у вас есть система частиц, лоды, террейн рендеринг, порталы, костевая анимация, деколи? Столько вопросов по реализации )
Apetrus
21.04.2016 02:05Буду рад, пытайте :) Частицы использовались в нескольких проектах, а значит пора их выносить в модуль. Лоды, скелетная анимация и горы тоже были, но только в одном проекте, поэтому пока выносить их в движок не спешу. Стоит уточнить, что у меня нет задачи создать универсальный движок. Появляются новые проекты и моя цель максимально ускорить процесс создания игры с минимальным портированием.
Apetrus
21.04.2016 01:42+1Пожалуй mainloop — это самое важное, что есть в основном классе. Именно там происходит управление fps, обработка тачей/кнопок, выполнение тасков в основном потоке (особенно для OpenGL без shared context актуально и изменений UI из фоновых потоков), поддержка слоев (аля окна), смена стейтов приложения (меню/уровень/магазин/.../лоадинг скрин). Видимо это еще одна тема для статьи :)
agent10
21.04.2016 08:42А зачем писать именно с нуля? Есть так хочется своих кастомных вещей в движке, то почему бы не взять открытый существующий и просто менять/расширять его как душе вздумается?
ANTPro
21.04.2016 09:22+1Где можно посмотреть на игры сделанные на этом движке? А то может там тетрис или flappybird :)
pda0
21.04.2016 18:34-1Такой вопрос, кстати. Известно, что в играх порой очень сложно обойтись без скриптов, увеличивающих гибкость движка. И как с этим на iOS, где любые интерпретаторы запрещены? lua, так понимаю, не используешь?
Apetrus
21.04.2016 18:39Особой необходимости в скриптах пока не было. Вернее, уровни созданные в левел-билдере на webGL конвертировались в логику на С++. Но это была внутренняя логика игры и в движок ее не выносил. Если понадобятся в будущем скрипты, то буду использовать Lua. В AppStore вроде использовать Lua не запрещено.
Nagg
21.04.2016 20:28+2Если вы будете скачивать скрипты на Lua из интернетов и запускать — тогда эпл имеет право вас забанить (есть в апп сторе такой пункт), а так — на здоровье.
mkarev
21.04.2016 20:39+1ЕМНИП запрещено интерпретировать код, загруженный в обход аппстора. Если интерпретируемый код содержится в бандле приложения, то проблем быть не должно.
stepanp
Исходники покажете?
Ну и без C++11 писать это боль все-таки.
Долго весь движок писали? Один?
Apetrus
Целиком исходники не покажу, но отдельные модули буду скидывать в след. статьях. Например модуль ReplayKit и модуль для захвата видео игры под OSX (при чем 1080p 60fps, что помогло в создании промо-видео к игре), выложу полностью. Да, писал один. Последняя глобальная переделка движка была в 2014г. В посте ниже подробнее описал историю создания движка.