XNA
GameDev.ru / Сообщества / XNA / Статьи / XNA Content Pipeline: Обзор

XNA Content Pipeline: Обзор

Автор:

Большинство начинающих разработчиков-программистов (классический вариант),  поначалу, основное внимание концентрируют на коде, алгоритмах, технологиях, пытаясь создать свои движки и библиотеки. А их «игры» — в основном, только код. Но со временем они начинают понимать, что игра — это только отчасти код. И что есть ещё другая важная часть, без которой полноценной игры быть не может, — контент.

Контент (от англ. content — содержимое) в данном случае — это текстуры, модели, анимация, звук, музыка и т.д. То есть, наполнение игры.

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

В этой статье речь пойдет о том, как в XNA принято работать с контентом, а конкретнее — о XNA Content Pipeline.

XNA Content Pipeline
Основная идея
Подробней об устройстве XNA Content Pipeline
Расширение XNA content pipeline
Интеграция с Visual Studio
Недостатки
В завершение
Полезные ссылки

XNA Content Pipeline

Большинство разработчиков  представляет себе загрузку моделей, текстур или другого контента как метод или функцию, например, LoadTexture или LoadModel. Такие функции ориентированы на какой-то определенный формат, знают, как прочитать из него данные и как их впоследствии разместить в памяти, чтобы программа могла с ними дальше работать. Код этой функции содержится в самой программе (игре). Программа сообщает функции имя файла, а функция соответственно выполняет загрузку.

В XNA по умолчанию подход  совсем другой. В XNA в этом месте за дело берется XNA Content Pipeline. Content Pipeline можно перевести на русский язык как «конвейер контента» (имеется в виду, система, в которой данные проходят последовательно несколько стадий, на каждой из которых над данными выполняется какое-то действие).

XNA Content Pipeline — это инструмент в форме сборок .net (библиотек) и, следовательно, .net классов. Этот инструмент в первую очередь интегрируется в среду разработки Visual Studio, но помимо этого представлен и классами на стороне библиотеки времени исполнения XNA (XNA runtime). Другими словами, XNA Content Pipeline — это общее название для классов, которые интегрируются в среду разработки, и классов, которые используются в конечной программе для загрузки контента.

Изложенное возможно звучит непонятно, но если рассмотреть далее идеи и устройство системы в деталях – все станет на свои места.

Основная идея

Конечно, абсолютно точно можно сказать, какие же цели преследовались, когда создавался XNA Content Pipeline, могут только разработчики XNA. Но можно попробовать сделать собственные выводы, исходя из того, что реально получилось, и того, как оно устроено.

Итак, что дает XNA Content Pipeline.

В первую очередь, часть работы перенесена на время компиляции. В отличие от варианта с единым методом LoadTexture, который все действия выполняет во время работы программы, с XNA Content Pipeline часть работы выполняется в среде разработки во время компиляции. Конкретнее, на этапе компиляции данные извлекаютя (import) из первоначального формата (x, fbx и т.д. — для моделей; png, bmp, dds, jpg и т.д. — для изображений; wav, mp3 — для аудио), кроме того, для каждого типа данных (уже независимо от первоначального файлового формата) выполняется предварительная обработка (processing). А уже во время выполнения (run-time), приложению не приходится работать с разными форматами, и выполнять все эти действие, потому что результатом работы Content Pipeline на этапе компиляции есть файлы внутреннего формата (XNB), которые, по сути, есть бинарно сериализованными классами приложения (что означает — минимум затрат времени при загрузке).

Исходя из этого, мы сразу получаем и вторую идею — возможность отделить остальную логику приложения от того, в каком формате контент нам был предоставлен первоначально. Только самая первая часть «конвейера» знает о файловых форматах. И после того, как на первой стадии данные извлекутся из файла, они будут в едином представлении, которое зависит от типа самих данных, а не от первоначального формата.

И, конечно же, в XNA Content Pipeline предусмотрена расширяемость. В этом всем не было бы смысла, если бы мы не могли заставить работать систему с теми форматами, которые нам нужны, и выполнять свою специфическую обработку.

Подробней  об устройстве XNA Content Pipeline

Как выше было сказано, в первую очередь всю систему можно разделить на две части: ту, что интегрируется в студию и выполняет свою работу во время компиляции XNA проекта, и ту часть (Content Manager), которая работает во время выполнения XNA приложения (классы и методы, которые мы используем в коде нашей игры).

Для наглядности позаимствуем диаграмму с блога команды разработчиков XNA.

XNAPipelineArticle_p1 | XNA Content Pipeline: Обзор

Спускаясь на уровень глубже, этап компиляции можно разделить на такие части: импорт, предварительная обработка, сериализация в XNB.

На этапе импорта (content importer) из конкретного файлового формата данные извлекаются и трансформируются во внутреннее представление (content DOM). Как уже упоминалось выше, представление зависит от типа данных, а нет от файлового формата. Так данные из .x файла будут представлены точно так же, как и данные из .fbx (вертексы с текстурными координатами, нормалями и т.д.), так как оба формата содержать 3D-модель.

На втором этапе (content processor) над данными может проводиться дополнительная обработка, какая именно — зависит от ваших потребностей. Но в любом случае, даже если с данными ничего делать не нужно, то на этом этапе как минимум определяется, в виде какого класса данные будут представлены в приложении. Хорошим примером предварительной обработки, может быть генерация карт освещения (lightmaps) или  генерация упрощенных LOD-моделей на основе оригинальной. Или же, если вам просто нужно преобразовать данные модели в какой-то свой особый тип (вместо стандартного Model, например).

После того, как тип (класс) для данных определен, Content Pipeline создает экземпляр данного типа, заполняет его данными, и выполняет сериализацию этого экземпляра в файл формата XNB.

А вот уже во время выполнения приложения, происходит меньше всего действий. Тут по требованию программиста (благодаря коду, который он написал) Content Manager пытается открыть соответствующий XNB файл и десериализовать экземпляр соответствующего класса.

public class Game1 : Microsoft.Xna.Framework.Game
{    
       Model myPrettyModel;

       protected override void LoadContent()
       {
               myPrettyModel =     Content.Load<Model>("myPrettyModelFile");
       }
}

Сам формат XNB не задокументирован. Он просто не рассчитан на модификацию, к тому же, по словам разработчиков из команды XNA, часто меняется. Но как они сами говорят, в формате нет ничего секретного, и при желании можно взять Reflector (посмотреть код классов, которые осуществляют генерацию файлов) или любой hex-редактор и посмотреть, что же и как туда пишется. И если мы это сделаем, то увидим, что помимо «магических чисел» — это просто последовательность из имен типов (классов) и бинарно сериализованных экземпляров этих типов (как уже было сказано).

Таким образом, когда вы вызовете generic метод ContentManager.Load для класса Model, класс ContentManager попытается десериализовать из указанной вами единицы контента (из XNB файла) экземпляр класса Model. И если во время компиляции был выбран правильный соответствующий модуль обработки (content processor), то вы получите свою модель.

Расширение XNA content pipeline

В первую очередь, возможность модификации обеспечивается тем, что два основных этапа — импорт и обработка — вынесены в модули. Именно поэтому на диаграмме эти узлы представлены несколькими экземплярами. Если вы ещё не догадались, для каждого формата существует свой отдельный content importer, а для каждого вида данных и каждого конечного типа — свой content processor.

То, какую именно комбинацию из двух модулей нужно использовать для каждой конкретной единицы контента, задает разработчик прямо из среды разработки (что и есть одним из элементов интеграции).

Помимо этого, есть также возможность добавлять свои типы времени выполнения. Так скажем, если вам не нравится стандартный класс Model, то вы можете описать свой, при этом обеспечив для него поддержку со стороны Content Pipeline. Правда, для этого вам придется создать свой content processor, но это уже тема для отдельной статьи (про TypeWriter, TypeReader, автоматическую сериализацию и написание своих модулей).

Интеграция с Visual Studio

XNA Content Pipeline интегрируется в среду разработки как часть XNA Game Studio. И представляет собой отдельную папку «Content» в проекте XNA приложения в версии XNA 3.1. Или же отдельный проект, начиная с версии XNA 4.0.

XNAPipelineArticle_p2 | XNA Content Pipeline: Обзор

Слева на рисунке XNA 4.0 в Visual Studio 2010, справа — XNA 3.1 в Visual Studio 2010.

Все добавленные в такую папку (или проект) файлы расцениваются как отдельные единицы контента. И при выборе такого проекта, на панели Propeties (Свойства) отдельно отображаются параметры, связанные с XNA Content Pipeline, в том числе назначенные модули для импорта и обработки. И для конкретного модуля обработки — свои специфические параметры.

XNAPipelineArticle_p3 | XNA Content Pipeline: Обзор

При этом такая папка (или проект) имеет свой список «References», сборки из которого не только используются во время компиляции, но также используются как источник модулей (content importer, content processor) для конвейера. Когда возникает необходимость показать список доступных модулей, content pipeline сканирует сборки, добавленные в «References», на наличие классов-модулей конвейера (определяет по базовому классу или интерфейсу).

Такой подход на первый взгляд довольно необычен. Но если мы посмотрим внимательно, то увидим, что аналогичные вещи есть и у профессиональных разработчиков. Хороший пример – Source Engine от Valve, в котором есть внешний простой текстовый формат SMD и внутренний формат MDL, который (по слухам) полностью повторяет то, как данные располагаются в памяти программы во время работы.

Недостатки

Недостатки очевидны. В первую очередь, с этой системой значительно труднее освоиться, чем с одним «плоским» методом LoadTexture или LoadModel.

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

Но не стоит забывать, XNA это не только платформа Windows, это также XBOX и Windows Phone 7. И на этих двух платформах скорость загрузки контента более критична. К тому же, Content Pipeline помогает логически отделить код вашей игры от кода, который обеспечивает работу с файлами и устройствами хранения данных (который меняется от платформы к платформе).

В то же время, если вы работаете только под Windows, никто не мешает работать по старинке — написать свои методы загрузки моделей, текстур и звуков, используя пространство имен System.IO.

В завершение

Вот и все. Статья, возможно, получилась не совсем техническая. Но мне хотелось сделать больше обзор, чем «кучу кода с комментариями». Если кому-то будет интересно, возможно в недалеком будущем я попробую продолжить тему, но уже с конкретными примерами кода — модулей импорта и обработки.

Полезные ссылки

Интересующимся, в первую очередь, стоит заглянуть сюда:
Официальный портал Microsoft - посвященный разработке под XNA  (недавно сменил дизайн и адрес – в связи с выходом Windows Phone 7).
Официальный блог команды разработчиков XNA.
Персональный блог товарища по имени Shawn Hargreaves (участвует в разработке XNA Framework)

Конкретнее по XNA Content Pipeline:
Про структуру Content Pipeline, а также про создание своих типов.
Полезная диаграмма стандартных типов и модулей Content Pipeline.
Примеры от Microsoft связанные с Content Pipeline
Мой пример модуля импорта для Valve SMD =)

17 января 2011

#content, #content pipeline, #C#, #XNA, #XNB, #.NET

2001—2017 © GameDev.ru — Разработка игр