'

Архитектурные особенности систем разработки консольных и многоплатформенных игр

Понравилась презентация – покажи это...





Слайд 0

Архитектурные особенности систем разработки консольных и многоплатформенных игр Руслан Абдикеев Jaleco Entertainment mailto: ruslan@vr1.spb.ru http://www.jaleco.com


Слайд 1

Системы разработки игр Задачи и характеристики Типичные архитектурные решения Ключевые проблемы Пути решения проблем Примеры


Слайд 2

Уточнение Разработка консольных или кроссплатформенных игр – огромная тема Планирование Организация производственного процесса Подготовка контента Переносимость кода и функциональности Ограничения конкретных консолей Различия в TCR/TRC


Слайд 3

Уточнение (2) Консоли предъявляют жесткие требования к распределению ресурсов во время игры Консольные игры предполагают более жесткий quality assurance и более жесткие требования к целостности контента Решения, применяемые в современных технологических цепочках, не лучшим образом сказываются на масштабируемости и производительности движка


Слайд 4

Уточнение (3) Основные темы: Снижение накладных расходов при сохранении гибкости и расширяемости архитектуры Способы эффективного представления контента в памяти и на диске Механизмы настройки контента Вопросы гарантии целостности ресурсов Большая часть материала применима и к разработке игры для одной платформы, в частности, для PC


Слайд 5

Мотивы создания систем разработки игр Отраслевые тенденции Ожидания потребителей Требования издателей


Слайд 6

Мотивация: отраслевые тенденции Стремительное развитие технологий Резкое усложнение игрового контента Самые разнообразные платформы «Переход количества в качество» Вытеснение «гаражных» методов разработки промышленными


Слайд 7

Мотивация: ожидания потребителей Покупают не технологию, а контент Но ожидают кинематографическое качество Большой и сложный мир, живущий сам по себе Выразительные и эмоциональные персонажи Предельная интерактивность Поведение, анимация, звук, изображение…


Слайд 8

Мотивация: требования издателей Минимум рисков, связанных с производством Сокращение цикла разработки для ускорения выхода на рынок Повторная используемость контента (перенос на другие платформы, сиквелы) Разумный бюджет Поддержка новых технологий - подразумевается


Слайд 9

Характеристики системы разработки игр Простота внедрения Гибкость и расширяемость Масштабируемость Производительность


Слайд 10

Задачи: простота внедрения Быстрое обучение Простые, но мощные средства Единообразные инструменты Автоматизация рутинных процессов Продвинутые средства отладки контента


Слайд 11

Задачи: гибкость, расширяемость Простота настройки контента Изменение данных «в последний момент» Внедрение новых технологий в сжатые сроки Быстрая проверка идей без цикла разработки/внедрения


Слайд 12

Задачи: масштабируемость Большие и сверхбольшие размеры контента Параллельное создание контента Параллельная разработка нескольких игр Поддержка нескольких платформ Поддержка нескольких жанров


Слайд 13

Задачи: производительность технологической цепочки Четкое разграничение проблем Разделение труда Принцип «наиболее квалифицированного специалиста» Уменьшение «зацепок» и простоев Сбалансированное распределение ответственности Сокращение цикла разработки


Слайд 14

Задачи: производительность движка Еще раз: большие и сверхбольшие размеры контента Еще раз: сложный мир, живущий сам по себе Мощные средства оптимизации контента Предсказуемое время отклика движка Быстрая фоновая загрузка контента


Слайд 15

Типичные архитектурные решения Абстрагирование, расцепление (decoupling) Компонентные архитектуры Архитектуры, управляемые данными Скрипты Обобщенный формат хранения контента «Добавление уровня косвенности решает большинство проблем»


Слайд 16

Компонентные архитектуры Подключаемые (plug-in) модули Сложные динамические системы из простых компонент Построение платформы для будущих расширений


Слайд 17

Компонентные архитектуры (2) Уменьшение «зацепления» кода Повторное использование компонент Много игр – одна кодовая база Композиция компонент – комбинаторный взрыв возможностей


Слайд 18

Data driven архитектуры Инкапсуляция структуры игрового мира и настроек его компонентов Основное время разработки игры – создание контента и его настройка Игра – база данных, не надо фиксировать ее в коде Оставьте программистов в покое! Быстрый цикл идея-проверка-внедрение


Слайд 19

Data driven архитектуры (2) Данными легче управлять, чем кодом У данных меньше «зацепление», чем у кода Схема: объект = шаблон + настройки Жестче схема – меньше ошибок Данные легче поддаются автоматической обработке: сборке ресурсов, переносу на другие платформы


Слайд 20

Data driven архитектуры (3) Единообразные инструменты Меньше инструментов – больше областей их применения Композиция атрибутов и структурная композиция – комбинаторный взрыв возможностей


Слайд 21

Скрипты Инкапсуляция поведения элементов системы Мы даже код сделаем данными™ Оставьте программистов в покое! Быстрый цикл идея-проверка-внедрение


Слайд 22

Скрипты (2) Высокоуровневые примитивы языка, специфичные для игровых задач Естественный доступ к функциональности движка Виртуальная машина, компактное представление, JIT-компиляция Перенос на другие платформы


Слайд 23

Обобщенный формат хранения Сериализация/десериализация Компоненты + данные Все равно, что, где и как хранить Единообразие и прозрачность Некоторые гарантии целостности


Слайд 24

Результаты Вроде бы, все хорошо Гибкость, расширяемость Производительность технологической цепочки Единообразный подход к компонентам игрового мира и их атрибутам Но…


Слайд 25

Пиррова победа? Чрезмерная общность Высокие издержки, «over engineering» Неформальные «договоренности» Явные спецификации недостаточно формализованы или отсутствуют Связи кода со внутренней структурой 3D моделей и сцен Что и как часто мы будем изменять или использовать Естественные последствия требований гибкости и расширяемости


Слайд 26

Пиррова победа? (2) Еще раз: большой и сложный мир… Высокая степень детализации контента Какова цена единообразного подхода к атрибутам системы? «Грубый помол»? Масштабируемость тех. цепочки? Масштабируемость движка? Какова цена неформальных договоренностей? Целостность ресурсов? Масштабируемость тех. цепочки? Производительность движка?


Слайд 27

Два детальных примера Хранение 3D сцен на диске и их представление в памяти Настройка контента и доступ к параметрам игровых объектов из игры


Слайд 28

Case 1: представление 3D моделей Типичные схемы: Монолитное представление Слишком жесткое, но эффективное Открытое (компонентное) представление Слишком гибкое, но расточительное и хрупкое Нам нужна золотая середина Не платим за то, что не используем Требуем гарантий целостности


Слайд 29

3D: монолитное представление «Классика жанра»: Quake При разработке движка фиксируется набор управляемых атрибутов конкретных моделей (как правило, только скины и анимации) Жесткие правила именования (тэги или имена подобъектов) При экспорте – преобразование в жестко заданный для этого типа моделей формат (Quake .MD3 для моделей игроков, например) У моделей не может быть развитой внутренней структуры нет разнородных атрибутов – нет проблемы


Слайд 30

3D: открытое представление Scene graph, DAG, дерево подобъектов Доступны все атрибуты всех объектов в иерархии (в т.ч. атрибуты шейдеров) Разнородные (гетерогенные) данные Обобщенный, независимый от типа модели и target платформы формат Произвольный доступ к модели со стороны игровых объектов Как правило, компонентная архитектура


Слайд 31

3D: открытое представление (2) Существуют правила именования подобъектов и их атрибутов Чаще всего, неявные правила («изустное творчество»), поскольку игровой код отделен от инструментов по созданию контента Неявные правила – неформальные договоренности


Слайд 32

3D: открытое представление (3) «Слишком» гибкое представление Наши намерения не выражены явно Весь спектр классических проблем ОО: Некомпактное представление объектов Медленная загрузка Обилие перераспределений памяти Фрагментация памяти Высокий расход памяти и медленное копирование объектов: движок не знает, какие данные мы можем изменить


Слайд 33

3D: открытое представление (4) Статические объекты могут быть представлены более эффективно Но оптимизатор не знает, что мы будем изменять Нет гарантий целостности ресурсов Пока есть «неформальные договоренности», автоматическая проверка целостности невозможна


Слайд 34

3D: открытое представление (5) Игровой код: уродлив, неэффективен, подвержен ошибкам // получение бокса левого закрылка render::node n = 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(); Заранее известно, что левый закрылок должен присутствовать в модели Известно, что нужен только бокс закрылка


Слайд 35

3D: открытое представление (6) Игровой код: хотелось бы что-то подобное: // получение бокса левого закрылка render::box b = Plane.FlapsLeft.box; Более красиво, более эффективно Целостность данных должна проверяться раньше, не во время исполнения


Слайд 36

Вседозволенность против гибкости Абстрагирование и позднее связывание вызывают потери неявной информации о структуре связей кода с контентом Она как бы есть, но скрытая, недоступная для внешних (управляемых данными) инструментов Для консольных игр такие потери плюс «неформальные договоренности» могут стать фатальными Сходная проблема есть в языках программирования как оборотная сторона ОО и как пример «abstraction penalty»


Слайд 37

Вседозволенность: Стек на 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


Слайд 38

Гибкость: Стек на 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 – ровно то, что нужно Эффективность: нет преобразований Целостность: ошибки типов диагностируются во время компиляции


Слайд 39

3D: явная параметризация Явная параметризация типов в языках программирования переносит время связывания на момент компиляции и дает: Эффективность Удобство Гарантии целостности При этом сохраняя гибкость и расширяемость


Слайд 40

3D: открытое представление (7) // получение бокса левого закрылка render::box b = Plane.FlapsLeft.box; Это не должен быть «синтаксический сахар» скриптового языка, скрывающий неэффективную и хрупкую реализацию Это должно быть именно представление модели в памяти, поэтому результаты должны быть применимы и к C++ коду Перенос времени связывания с run-time на момент компиляции кода (compile-time) и на момент сборки ресурсов (bundler-time) Ранняя и 100% диагностика ошибок


Слайд 41

3D: явная параметризация (2) Введем в движок описание 3D моделей: game FighterAce { scene Plane { instance LaGG3 “ЛаГГ-3”; instance P38L “P-38L”; readonly FlapsLeft.box = Mesh(“FlapsLeft”).Box; } } Описание задает как атрибуты объектов, используемые/модифицируемые игровым кодом, так и требования к 3D моделям


Слайд 42

3D: явная параметризация (3) Для архитектур, управляемых данными, – это естественный шаг Требования к модели используются при экспорте, оптимизации и сборке ресурсов Информация о связях игрового кода с 3D моделью используется для формирования эффективного memory layout и удобного интерфейса к объекту


Слайд 43

3D: назад к монолитам Фактически, мы возвращаемся к «жестким» монолитным форматам хранения 3D моделей Автоматически генерируемые форматы, «заточенные» под конкретную комбинацию модель-платформа Работа с этими форматами происходит прозрачно и полностью автоматически для игрового кода Внутренние атрибуты моделей доступны в естественной для игрового кода форме


Слайд 44

3D: memory layout В соответствии с описанием 3D модели, все ее атрибуты разбиваются на два класса: Разделяемые атрибуты, общие для всех копий Копируемые атрибуты, в частности, атрибуты, изменяемые игровым кодом Мы можем распределить всю 3D модель (исключая ресурсы рендера) в два непрерывных участка памяти Компактное, cache friendly представление Идеально для фоновой загрузки и копирования


Слайд 45

3D: детали Автоматически генерируемый для игровых объектов интерфейс работы с 3D моделью (общая идея): class Plane : public render::u_object { public: struct FlapsLeft_t { render::box8 box; }; enum instance_t { LaGG3, P38L } instance; FlapsLeft_t FlapsLeft; static Plane* Load( instance_t inst ); Plane* Copy() const; void Destroy(); };


Слайд 46

3D: детали (2) Формат представления конкретной модели определяется файлом описания и собственно содержимым 3D сцены Для каждого файла описания генерируется .h и .cpp .cpp – assertions и зависящий от описания модели код На диске данные представлены в готовом к употреблению виде (относительная адресация) с таблицей fix up Загрузка – чтение с диска в непрерывный участок памяти, применение fix up и создание C++ объектов (placement new)


Слайд 47

3D: детали (3) Движок (включая рендер) работает со структурой, близкой к scene graph Объекты движка не имеют фиксированного memory layout (любой атрибут может попасть в пользовательскую область) Адресация через аналог v-table Тем не менее, естественная C++ нотация (благодаря средствам языка вроде template) Скорость работы движка выше из-за дружественной к кэшу структуры


Слайд 48

3D: голые факты Считаем только пользовательскую часть, без звуков и 3D ресурсов Не самый худший случай Один самолет B-17 (без панели кабины) Около 50 подобъектов, анимированных художниками Около 100 атрибутов, изменяемых из игры (преимущественно матрицы трансформации, флаги видимости, контроллеры анимаций)


Слайд 49

3D: голые факты Вседозволенность: Одна копия: около 80K в 140 блоках Разделяемые данные: около 700K в 170 блоках Создание копии на PC вызывает легкие лаги и swap при недостаточном объеме памяти Применялись аллокаторы, умные указатели и т.п. Гибкость с явными намерениями: Одна копия: 12K в 1 блоке Разделяемые данные: 256K в 1 блоке Прямое чтение с диска с последующим fix up Скорость копирования сравнима с memcpy() Никаких аллокаторов (выделение по 4K) Никаких умных указателей


Слайд 50

Case 1: результаты Высокая производительность Минимальные требования к ресурсам Отсутствие сложного lifetime management Эффективная работа с памятью Полностью автоматическая сборка 100% гарантии целостности ресурсов Ясный, простой и эффективный игровой код Сохранены гибкость и расширяемость


Слайд 51

Case 1: заключение Обобщенный формат используется для хранения и трансформаций контента Сборщик ресурсов Выполняет низкоуровневую оптимизацию под конкретную платформу в соответствии с описанием модели и конкретной 3D сценой Записывает в низкоуровневом формате, «заточенном» под 3D сцену и платформу Игра работает с семейством низкоуровневых форматов Это происходит автоматически и прозрачно для игрового кода и разработчиков


Слайд 52

Case 2: настройка контента Художник создает модель и указывает ее рабочие характеристики Игровой код активно использует данные, специфичные для данной модели (мы предполагаем доступ к настройкам «только для чтения») Требования Гарантии корректности данных Эффективный и удобный доступ к данным Автоматическая обратная совместимость


Слайд 53

Настройка: о чем речь? В идеале, художник прямо в пакете моделирования указывает параметры:


Слайд 54

Настройка: о чем речь? (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 }


Слайд 55

Настройка: о чем речь? (3) Впрочем, стандартный инструмент удобнее:


Слайд 56

Настройка: о чем речь? (3) Игровому коду эта информация должна быть доступна естественными (языковыми) средствами:


Слайд 57

Настройка: типичные схемы Пары «имя» – «значение» Предельная гибкость, хрупкость и неэффективность Громоздкий код Сериализация/десериализация Ясный и надежный код Гарантии целостности Потенциально – гибкость, расширяемость и эффективность


Слайд 58

Настройка: сериализация Сериализация/десериализация Описание библиотек типов Инструменты для редактирования Сборщик, выполняющий проверку валидности входных данных и сериализацию в финальный формат Если в системе разработки есть такие возможности, это уже хорошо!


Слайд 59

Настройка: подводные камни Обратная совместимость и расширяемость схемы Эффективное представление в runtime Быстрая загрузка Минимальный расход памяти


Слайд 60

Настройка: обратная совместимость Следует отличать «значения по умолчанию» от введенных значений Это позволит изменить значения по умолчанию сразу для большого класса объектов Экземпляр = шаблон + переопределенные значения Готовность к расширению схемы


Слайд 61

Настройка: применение шаблонов Явное описание структуры компонента Указываются типы и значения по умолчанию Не имеющие умолчаний поля являются обязательными <Template name="PLANE_GUN"> <Attr name="AmmoModel" type="objref"?/> <Attr name="MinDist" type="float" value="0"/> <Attr name="MaxDist" type="float" value="1000"/> <Attr name="FullAmmoPack" type="int" value="100"/> </Template>


Слайд 62

Настройка: применение шаблонов (2) Конкретный экземпляр компонента «наследуется» от шаблона и переопределяет значения полей Обязательны значения обязательных полей Нельзя вводить новые поля Нельзя указывать тип полей <Component name="GunControl" template="PLANE_GUN"> <Attr name="AmmoModel" value="mod/ammo"/> <Attr name="MaxDist" value="1000"/> </Component>


Слайд 63

Настройка: применение шаблонов (3) Можно использовать промежуточные шаблоны (единица крупных замен значений по умолчанию) Промежуточные шаблоны «наследуются» от других шаблонов и переопределяют значения полей Нельзя вводить новые поля Нельзя указывать тип полей


Слайд 64

Настройка: библиотека типов Чтение всех «корневых» шаблонов (их изменение, как правило, производит игровой программист) Генерация библиотек типов для внешних (управляемых данными) инструментов Генерация C++ описаний компонентов для эффективной работы в runtime конкретной платформы


Слайд 65

Настройка: библиотека типов (2) Пример сгенерированного C++ объявления: struct PLANE_GUN { render::obj_ref<AMMO_MODEL> AmmoModel; float MinDistance; float MaxDistance; int FullAmmoPack; sdf::t_carray<id_value_pair> g_Other; };


Слайд 66

Настройка: редактирование Расширяемый набор управляющих элементов для платформы разработчика (PC, Win32) Стандартные механизмы библиотеки типов и сериализации/десериализации Возможность создавать промежуточные шаблоны Явная визуальная дифференциация значений по умолчанию и явно заданных значений Работа с больших количеством промежуточных форматов (XML, обобщенные форматы хранения 3D сцен, etc)


Слайд 67

Настройка: сборщик Читает все шаблоны и компоненты из промежуточных форматов и библиотек типов Диагностирует разночтения библиотеки типов и «корневых» шаблонов Формирует полные (плоские) компоненты Диагностирует отсутствующие обязательные поля или поля, отсутствующие в схеме При необходимости, формирует дополнительные массивы пар «имя» – «значение»


Слайд 68

Настройка: сборщик (2) Сериализует данные в финальный, зависящий от target платформы, формат Оптимизирует размещение (данные «только для чтения») Исключает дубликаты Формирует плоское представление сложных типов (строк, массивов, ресурсных указателей) В финальном формате данные на диске хранятся в готовом к употреблению виде Выделение непрерывной области памяти, чтение с диска, fix up, использование


Слайд 69

Настройка: детали Для плоского и относительно-адресуемого представления сложных типов используются те же типы, что и в 3D сценах template<typename T> struct t_carray { // Интерфейс как у константного std::vector const T* begin() const { return ShiftPointer<T>(this, iBegin); } // ... private: std::ptrdiff_t iBegin, iEnd; };


Слайд 70

Настройка: детали (2) Результирующий (автоматически сгенерированный и библиотечный) код непереносим, а финальные бинарные форматы «заточены» под конкретную пару компилятор-платформа Для другой пары компилятор-платформа будет сгенерирован другой код и построены другие форматы Может быть изменен порядок байт и выполнено строгое выравнивание Все это произойдет автоматически


Слайд 71

Case 2: результаты Высокая производительность Минимальные требования к ресурсам Эффективная работа с памятью Полностью автоматическая сборка 100% гарантии целостности ресурсов Ясный, простой и эффективный игровой код Сохранены гибкость и расширяемость


Слайд 72

Case 2: заключение Обобщенные форматы (XML…) используются для хранения и редактирования контента Сборщик ресурсов Выполняет низкоуровневую оптимизацию под конкретную платформу в соответствии с описанием «корневых» шаблонов Записывает в низкоуровневом формате, «заточенном» под платформу и компилятор Игра работает с семейством низкоуровневых форматов Это происходит автоматически и прозрачно для игрового кода и разработчиков


Слайд 73

Заключение Мы рассмотрели приемы сохранения гибкости и расширяемости системы в условиях ограниченных ресурсов без потери производительности и масштабируемости В двух приведенных примерах это достигалось явной демонстрацией намерений со стороны разработчика, что позволяло перенести время связывания с runtime на более ранние моменты Благодаря этим приемам мы получали 100% гарантии целостности ресурсов


Слайд 74

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


Слайд 75

Вопросы? mailto:ruslan@vr1.spb.ru http://aruslan.nm.ru Мой e-mail и домашняя страничка http://www.jaleco.com Компания, в которой я работаю http://www.gamedev.ru http://www.gamasutra.com


×

HTML:





Ссылка: