700 likes | 871 Views
Архитектурные особенности систем разработки консольных и многоплатформенных игр. Руслан Абдикеев Jaleco Entertainment mailto: ruslan@vr1.spb.ru http://www.jaleco.com. Системы разработки игр. Задачи и характеристики Типичные архитектурные решения Ключевые проблемы Пути решения проблем
E N D
Архитектурные особенности систем разработки консольных и многоплатформенных игр Руслан Абдикеев Jaleco Entertainment mailto: ruslan@vr1.spb.ru http://www.jaleco.com
Системы разработки игр • Задачи и характеристики • Типичные архитектурные решения • Ключевые проблемы • Пути решения проблем • Примеры
Уточнение • Разработка консольных или кроссплатформенных игр – огромная тема • Планирование • Организация производственного процесса • Подготовка контента • Переносимость кода и функциональности • Ограничения конкретных консолей • Различия в TCR/TRC
Уточнение (2) • Консоли предъявляют жесткие требования к распределению ресурсов во время игры • Консольные игры предполагают более жесткий quality assurance и более жесткие требования к целостности контента • Решения, применяемые в современных технологических цепочках, не лучшим образом сказываются на масштабируемости и производительности движка
Уточнение (3) • Основные темы: • Снижение накладных расходов при сохранении гибкости и расширяемости архитектуры • Способы эффективного представления контента в памяти и на диске • Механизмы настройки контента • Вопросы гарантии целостности ресурсов • Большая часть материала применима и к разработке игры для одной платформы, в частности, для PC
Мотивация: отраслевые тенденции • Стремительное развитие технологий • Резкое усложнение игрового контента • Самые разнообразные платформы • «Переход количества в качество» • Вытеснение «гаражных» методов разработки промышленными
Мотивация: ожидания потребителей • Покупают не технологию, а контент • Но ожидают кинематографическое качество • Большой и сложный мир, живущий сам по себе • Выразительные и эмоциональные персонажи • Предельная интерактивность • Поведение, анимация, звук, изображение…
Мотивация: требования издателей • Минимум рисков, связанных с производством • Сокращение цикла разработки для ускорения выхода на рынок • Повторная используемость контента (перенос на другие платформы, сиквелы) • Разумный бюджет • Поддержка новых технологий - подразумевается
Характеристики системы разработки игр • Простота внедрения • Гибкость и расширяемость • Масштабируемость • Производительность
Типичные архитектурные решения • Абстрагирование, расцепление (decoupling) • Компонентные архитектуры • Архитектуры, управляемые данными • Скрипты • Обобщенный формат хранения контента • «Добавление уровня косвенности решает большинство проблем»
Компонентные архитектуры • Подключаемые (plug-in) модули • Сложные динамические системы из простых компонент • Построение платформы для будущих расширений
Компонентные архитектуры (2) • Уменьшение «зацепления» кода • Повторное использование компонент • Много игр – одна кодовая база • Композиция компонент – комбинаторный взрыв возможностей
Data driven архитектуры • Инкапсуляция структуры игрового мираи настроек его компонентов • Основное время разработки игры – создание контента и его настройка • Игра – база данных, не надо фиксировать ее в коде • Оставьте программистов в покое! • Быстрый цикл идея-проверка-внедрение
Data driven архитектуры (2) • Данными легче управлять, чем кодом • У данных меньше «зацепление»,чем у кода • Схема: объект = шаблон + настройки • Жестче схема – меньше ошибок • Данные легче поддаются автоматической обработке: сборке ресурсов, переносу на другие платформы
Data driven архитектуры (3) • Единообразные инструменты • Меньше инструментов – больше областей их применения • Композиция атрибутов и структурная композиция – комбинаторный взрыв возможностей
Скрипты • Инкапсуляция поведения элементов системы • Мы даже код сделаем данными™ • Оставьте программистов в покое! • Быстрый цикл идея-проверка-внедрение
Скрипты (2) • Высокоуровневые примитивы языка, специфичные для игровых задач • Естественный доступ к функциональности движка • Виртуальная машина, компактное представление, JIT-компиляция • Перенос на другие платформы
Обобщенный формат хранения • Сериализация/десериализация • Компоненты + данные • Все равно, что, где и как хранить • Единообразие и прозрачность • Некоторые гарантии целостности
Результаты • Вроде бы, все хорошо • Гибкость, расширяемость • Производительность технологической цепочки • Единообразный подход к компонентам игрового мира и их атрибутам • Но…
Пиррова победа? • Чрезмерная общность • Высокие издержки, «over engineering» • Неформальные «договоренности» • Явные спецификации недостаточно формализованы или отсутствуют • Связи кода со внутренней структурой 3D моделей и сцен • Что и как часто мы будем изменять или использовать • Естественные последствия требований гибкости и расширяемости
Пиррова победа? (2) • Еще раз: большой и сложный мир… • Высокая степень детализации контента • Какова цена единообразного подхода к атрибутам системы? • «Грубый помол»? • Масштабируемость тех. цепочки? • Масштабируемость движка? • Какова цена неформальных договоренностей? • Целостность ресурсов? • Масштабируемость тех. цепочки? • Производительность движка?
Два детальных примера • Хранение 3D сцен на диске и их представление в памяти • Настройка контента и доступ к параметрам игровых объектов из игры
Case 1: представление 3D моделей • Типичные схемы: • Монолитное представление • Слишком жесткое, но эффективное • Открытое (компонентное) представление • Слишком гибкое, но расточительное и хрупкое • Нам нужна золотая середина • Не платим за то, что не используем • Требуем гарантий целостности
3D: монолитное представление • «Классика жанра»: Quake • При разработке движка фиксируется набор управляемых атрибутов конкретных моделей (как правило, только скины и анимации) • Жесткие правила именования (тэги или имена подобъектов) • При экспорте – преобразование в жестко заданный для этого типа моделей формат (Quake .MD3для моделей игроков, например) • У моделей не может быть развитой внутренней структуры • нет разнородных атрибутов – нет проблемы
3D: открытое представление • Scene graph, DAG, дерево подобъектов • Доступны все атрибуты всех объектов в иерархии (в т.ч. атрибуты шейдеров) • Разнородные (гетерогенные) данные • Обобщенный, независимый от типа модели и target платформы формат • Произвольный доступ к модели со стороны игровых объектов • Как правило, компонентная архитектура
3D: открытое представление (2) • Существуют правила именования подобъектов и их атрибутов • Чаще всего, неявные правила («изустное творчество»), поскольку игровой код отделен от инструментов по созданию контента • Неявные правила – неформальные договоренности
3D: открытое представление (3) • «Слишком» гибкое представление • Наши намерения не выражены явно • Весь спектр классических проблем ОО: • Некомпактное представление объектов • Медленная загрузка • Обилие перераспределений памяти • Фрагментация памяти • Высокий расход памяти и медленное копирование объектов: движок не знает, какие данные мы можем изменить
3D: открытое представление (4) • Статические объекты могут быть представлены более эффективно • Но оптимизатор не знает, что мы будем изменять • Нет гарантий целостности ресурсов • Пока есть «неформальные договоренности», автоматическая проверка целостности невозможна
3D: открытое представление (5) • Игровой код: уродлив, неэффективен, подвержен ошибкам // получение бокса левого закрылка render::noden = Plane.find_node_recursive(“FlapsLeft”); assert( n != null ); render::mesh m = (render::mesh)n.get_robject(); assert( m != null ); render::box b = m.get_box(); • Заранее известно, что левый закрылок должен присутствовать в модели • Известно, что нужен только бокс закрылка
3D: открытое представление (6) • Игровой код: хотелось бы что-то подобное: // получение бокса левого закрылка render::boxb = Plane.FlapsLeft.box; • Более красиво, более эффективно • Целостность данных должна проверяться раньше, не во время исполнения
Вседозволенность против гибкости • Абстрагирование и позднее связывание вызывают потери неявной информации о структуре связей кода с контентом • Она как бы есть, но скрытая, недоступная для внешних (управляемых данными) инструментов • Для консольных игр такие потери плюс «неформальные договоренности» могут стать фатальными • Сходная проблема есть в языках программирования как оборотная сторона ОО и как пример «abstraction penalty»
Вседозволенность: Стек на C# class Stack { public void Push( object item ); public object Pop(); } Stack s = new Stack(); s.Push( (object)123 ); int n = (int)s.Pop(); • Слишком гибок: стек любых объектов • Не соответствует задаче: нам нужен стек int • Дорог и некрасив: преобразование в/из object’а • Не дает гарантий целостности: о несоответствии типов станет известно только в run-time
Гибкость: Стек на C# с Generics class Stack<T> { public void Push( T item ); public T Pop(); } Stack<int> s = new Stack<int>(); s.Push( 123 ); int n = s.Pop(); • «Заточенность»: стек int – ровно то, что нужно • Эффективность: нет преобразований • Целостность: ошибки типов диагностируются во время компиляции
3D: явная параметризация • Явная параметризация типов в языках программирования переносит время связывания на момент компиляции и дает: • Эффективность • Удобство • Гарантии целостности • При этом сохраняя гибкость и расширяемость
3D: открытое представление (7) // получение бокса левого закрылка render::boxb = Plane.FlapsLeft.box; • Это не должен быть «синтаксический сахар» скриптового языка, скрывающий неэффективную и хрупкую реализацию • Это должно быть именно представление модели в памяти, поэтому результаты должны быть применимы и к C++ коду • Перенос времени связывания с run-time на момент компиляции кода (compile-time) и на момент сборки ресурсов (bundler-time) • Ранняя и 100% диагностика ошибок
3D: явная параметризация (2) • Введем в движок описание 3D моделей: game FighterAce { scene Plane { instance LaGG3 “ЛаГГ-3”; instance P38L “P-38L”; readonly FlapsLeft.box = Mesh(“FlapsLeft”).Box; } } • Описание задает как атрибуты объектов, используемые/модифицируемые игровым кодом, так и требования к 3D моделям
3D: явная параметризация (3) • Для архитектур, управляемых данными, – это естественный шаг • Требования к модели используются при экспорте, оптимизации и сборке ресурсов • Информация о связях игрового кода с 3D моделью используется для формирования эффективного memory layout и удобного интерфейса к объекту
3D: назад к монолитам • Фактически, мы возвращаемся к «жестким» монолитным форматам хранения 3D моделей • Автоматически генерируемые форматы, «заточенные» под конкретную комбинацию модель-платформа • Работа с этимиформатами происходит прозрачно и полностью автоматически для игрового кода • Внутренние атрибуты моделей доступны в естественной для игрового кода форме
3D: memory layout • В соответствии с описанием 3D модели, все ее атрибуты разбиваются на два класса: • Разделяемые атрибуты, общие для всех копий • Копируемые атрибуты, в частности, атрибуты, изменяемые игровым кодом • Мы можем распределить всю 3D модель (исключая ресурсы рендера) в два непрерывных участка памяти • Компактное, cache friendly представление • Идеально для фоновой загрузки и копирования
3D: детали • Автоматически генерируемый для игровых объектов интерфейс работы с 3D моделью (общая идея): classPlane : public render::u_object { public: struct FlapsLeft_t { render::box8 box; }; enum instance_t { LaGG3, P38L } instance; FlapsLeft_t FlapsLeft; staticPlane* Load( instance_t inst ); Plane* Copy() const; voidDestroy(); };
3D: детали (2) • Формат представления конкретной модели определяется файлом описания и собственно содержимым 3D сцены • Для каждого файла описания генерируется .h и .cpp • .cpp – assertions и зависящий от описания моделикод • На диске данные представлены в готовом к употреблению виде (относительная адресация) с таблицей fix up • Загрузка – чтение с диска в непрерывный участок памяти, применение fix up и создание C++ объектов(placement new)
3D: детали (3) • Движок (включая рендер) работает со структурой, близкой к scene graph • Объекты движка не имеют фиксированного memory layout (любой атрибут может попасть в пользовательскую область) • Адресация через аналог v-table • Тем не менее, естественная C++ нотация (благодаря средствам языка вроде template) • Скорость работы движка выше из-за дружественной к кэшу структуры
3D: голые факты • Считаем только пользовательскую часть, без звуков и 3D ресурсов • Не самый худший случай • Один самолет B-17 (без панели кабины) • Около 50 подобъектов, анимированных художниками • Около 100 атрибутов, изменяемых из игры (преимущественно матрицы трансформации, флаги видимости, контроллеры анимаций)
3D: голые факты • Вседозволенность: • Одна копия: около 80K в 140 блоках • Разделяемые данные: около 700K в 170 блоках • Создание копии на PC вызывает легкие лаги и swap при недостаточном объеме памяти • Применялись аллокаторы, умные указатели и т.п. • Гибкость с явными намерениями: • Одна копия: 12K в 1 блоке • Разделяемые данные: 256K в 1 блоке • Прямое чтение с диска с последующим fix up • Скорость копирования сравнима с memcpy() • Никаких аллокаторов (выделение по 4K) • Никаких умных указателей
Case 1: результаты • Высокая производительность • Минимальные требования к ресурсам • Отсутствие сложного lifetime management • Эффективная работа с памятью • Полностью автоматическая сборка • 100% гарантии целостности ресурсов • Ясный, простой и эффективный игровой код • Сохранены гибкость и расширяемость
Case 1: заключение • Обобщенный формат используется для хранения и трансформаций контента • Сборщик ресурсов • Выполняет низкоуровневую оптимизацию под конкретную платформу в соответствии с описанием модели и конкретной 3D сценой • Записывает в низкоуровневом формате, «заточенном» под 3D сцену и платформу • Игра работает с семейством низкоуровневых форматов • Это происходит автоматически и прозрачно для игрового кода и разработчиков
Case 2: настройка контента • Художник создает модель и указывает ее рабочие характеристики • Игровой код активно использует данные, специфичные для данной модели (мы предполагаем доступ к настройкам «только для чтения») • Требования • Гарантии корректности данных • Эффективный и удобный доступ к данным • Автоматическая обратная совместимость
Настройка: о чем речь? • В идеале, художник прямо в пакете моделирования указывает параметры:
Настройка: о чем речь? (2) • Для отладки и тестирования настройки могут быть вынесены в текстовые файлы • Как XML: <Component name="GunControl" template="PLANE_GUN"> <Attr name="AmmoModel" value="Bullets/ammo"/> <Attr name="MinDistance" value="4.5"/> <Attr name="MaxDistance" value="1000.0"/> <Attr name="FullAmmoPack" value="400"/> </Component> • Или как любой другой удобный формат: GunControl:PLANE_GUN { AmmoModel = "Bullets/ammo" MinDistance = 4.5 MaxDistance = 1000.0 FullAmmoPack = 400 }
Настройка: о чем речь? (3) • Впрочем, стандартный инструмент удобнее: