ФЭНДОМ


ВведениеПравить

Перед тем, как начать изучать directX 11, мы потратим немного времени на создание специального фреймворка. Фреймворк (от английского "framework", а в переводе – "каркас") – это обертка над каким-то функционалом, облегчающая его использование. Его написание поможет нам в будущем при изучении: нам не придется отвлекаться на сторонний функционал (создание окна, обработка клавиш и мыши и т.д.), кроме того мы спрячем часто повторяющийся код из уроков.

Но чтобы не усложнять, мы будем держать фреймворк минималистическим. В нем будет только то, что нужно для ближайших уроков.

Данный урок разделен на три части. Сейчас мы рассмотрим теорию. В следующем напишем код. А в последнем - проверим написанный код на работоспособность и совершим инициализацию Direct3D.

D3D11 FrameworkПравить

Назовем наш фреймворк вот так вот просто – D3D11 Framework. Фреймворк будет содержать весь тот код, который нам будет нужен для изучения урока. Этот код мы будем держать в статической библиотеке полностью отдельно от кода уроков.

Функционал D3D11 FrameworkПравить

На текущий момент наш фреймворк должен уметь делать следующее:

  • Создание окна
  • Обработка клавиатуры и мыши
  • Выполнение полного цикла работы приложения от старта, до завершения
  • Выполнение пользовательского кода
  • Логирование

Для начала этого будет достаточно. В будущем мы будем добавлять дополнительный функционал по мере необходимости. Кроме того изучив какую-то тему по DX, мы будем переносить ее в фреймворк, если это будет возможно или нужно.

О коде D3D11 FrameworkПравить

Мы будем использовать базовые принципы ООП. Конечно, многие уроки пишут, используя процедурную или функциональную парадигму, но код при этом получается страшный (на мой взгляд), и, когда пытаешься его применять, это не получается: слишком много связей и никакой расширяемости, так что толку на самом деле от таких уроков и нет, прочитав API и то больше поймешь, как и что использовать. Если не верите – откройте DXUT.

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

Мы будем использовать несколько языковых средств из нового стандарта по С++. Еще мы будем использовать STL. Кроме того я повсеместно использую #pragma once. Хотя эта директива и не входит в стандарт С++, но она поддерживается большинством современных компиляторов. Она заменяет страшненький include guard.

Важное замечание о коде фреймворкаПравить

Данный фреймворк не является примером правильной архитектуры. В цикле "DirectX шаг за шагом" я обучаю вас не тому, как строить идеальную архитектуру, а изучению DirectX. Поэтому для более опытных программистов код фреймворка может показаться ужасным, он такой и есть, и это сделано умышленно. Конечно, я бы мог написать качественный фреймворк строчек эдак под 10 тысяч, но вам бы захотелось такое ковырять? А зачем нужны эти 10 тысяч строчек, когда задачи фреймворка вмещаются всего в одну тысячу? Нет, я не измеряю качество кода количеством строк, но правильная архитектура, это множество абстракций и каждая из этих абстракций - лишний код. Так зачем писать этот лишний код, если к концу цикла уроков вы выбросите его?

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

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

Архитектура D3D11 FrameworkПравить

Как я писал – наш фреймворк хранится в виде статической библиотеки.

Условно разобьем нашу библиотеку на системы – Common, Util, Input, Render, Framework.

DirectX11SteptoStep1 1

Система Common – отвечает за общий код, на данный момент там только макросы прописаны, так что ее не рассматриваем отдельно.

Система Util – отвечает за вспомогательный код

Система Input – отвечает за работу с клавиатурой и мышью

Система Render – будет отвечать за работу с экраном

Framework – будет управлять всеми системами.

Эти системы условны – я их выделил, чтобы было удобней объяснять работу кода и чтобы его как-то упорядочить. Компилятор не знает об этих системах, это чисто условность для программиста.

Теперь рассмотрю каждую из систем.

Common и UtilПравить

Эти системы будут содержать вспомогательный код фреймворка. На данный момент в Common будут только общие макросы.

В систему Util будет входить класс Log. Этот класс будет вести логирование фреймворка. Задача этого класса – в любой момент выводить сообщение по требованию. Выводить мы будем в файл и в окно консоли, если оно есть. Кроме того, сделаем три типа сообщений – информационное, ошибка, и debug. Первое будет нужно для информирования. Второе используем в случае ошибок. Третье – это тоже что и информационное, но пишется только в debug. Для чего оно? Через него мы будем отслеживать процесс выполнения фреймворка. Почему же нам для этого не использовать первое? Дело в том, что логирование – это издержка выполнения, а запись в файл – это очень большая издержка. Но мы же хотим, чтобы наша система работала быстро… Можно вообще отказаться от лога, но тогда будет сложнее искать ошибки. Так вот – такой способ позволяет отказаться от записи в лог в релизе, когда мы найдем все ошибки. А в дебаге, в котором мы будем разрабатывать наш проект, можно спокойно вести логирование.

InputПравить

Данная система отвечает за работу с клавиатурой и мышью. В системе ввода у нас будут два основных класса – InputListener и InputMgr. Систему ввода можно сделать множеством вариантов. Я решил в ней задействовать паттерн «Слушатель», он же - «Наблюдатель » (англ. 'Listener'/'Observer').

Работать это будет так – InputMgr осматривает (точнее получает список событий от Windows) текущее состояние клавиатуры и мыши, и в случае, если было какое-то событие, оповещает всех своих наблюдателей (которые наследуются от InputListener) об этом событии, они же в свою очередь получив оповещение, выполняют определенный код. Сложно? Не волнуйтесь, в коде это намного проще, чем в описании.

То есть когда вы захотите написать обработку ввода, вы должны создать наследника InputListener, полиморфировать нужные методы обработки событий, написать нужное поведение в этих методах и добавить экземпляр вашего наследника InputListener в InputMgr. Все. Дальше фреймворк сам будет думать, когда и как вызвать вашу обработку ошибок.

Данной системой мы еще очень долго не будем пользоваться, я ее написал для ваших опытов.

RenderПравить

Отвечает за вывод на экран. Будет состоять из двух классов – Render и Window.

Window – создает обычное окно. Кроме того взаимодействует с InputMgr, оповещая его о событиях клавиатуры и мыши. Также отслеживает другие события связанные с окном (сворачивание, изменение размера и т.д.)

Render – отвечает за рисование в Window.


FrameworkПравить

Будет выражена единственным классом – Framework. Этот класс полностью отвечает за выполнение всего приложения.

Общее представление архитектурыПравить

Вот что у нас получается:

DirectX11SteptoStep1 2

Создаем статическую библиотеку в Visual Studio 2010Править

Данная часть статьи является дополнительной и предназначена для тех новичков которые уверено проигнорировали предыдущее предупреждение об требуемом опыте и решили все таки изучать сейчас DirectX 11 :)

Запустите Visual Studio 2010. У меня установлена русская версия, но думаю вы сможете найти нужные команды по аналогии.

Вызывайте команду создания нового проекта:

Create Project 1

В открывшемся окне пишите желаемое название (D3D Framework) и путь к исходникам. Разницы между Win32 и консольным приложением здесь нет и выбирайте любое.

Create Project 2

Жмите ОК. А вот следующее окно очень важное:

Create Project 31


Мы создаем статическую библиотеку (в английской версии она будет называться Static Library).

После того как библиотека будет создана в обозревателе решений (Solution Explorer) приведите все к такому виду:

Create Project 41

То есть удалите все файлы кроме stdafx.cpp и stdafx.h. Помните, в студии файлы можно исключить и удалить. Исключение означает что они убираются из проекта, но сами файлы при этом существуют в папке проекта. Удаление означает их полное удаление с жесткого диска и проекта.

Затем удалите все папки (одну вы не сможете удалить, у меня это "Внешние зависимости", как видите у нее другой значок), не забыв перед этим переместить stdafx.cpp и stdafx.h в D3D_Framework.

Затем создайте нужные папки, как видите для каждой системы своя папка.

Откройте stdafx.cpp и оставьте только эту строчку:

#include "stdafx.h"

Откройте stdafx.h и оставьте только это:

#pragma once

Дополнительные настройкиПравить

В общем, мы уже создали библиотеку. Но можно ее еще немного настроить для лучшего использования. Для начала для совсем начинающих сообщу одну вещь - проект можно собирать по умолчанию в debug и в release (на самом деле вы можете создавать свои собственные виды сборки, но это к теме не относится). Переключаться между debug и release можно здесь:

Create Project 5

Я не буду подробно объяснять в чем между ними разница. Просто скажу, что пока вы пишите код, собирайте и запускайте его в Debug (кстати, помните, я выше писал что у нас лог имеет одну команду пишушую в debug?). А когда захотите дать файл кому-то другому, собирайте в Release. Еще скажу что приложение собранное в Release работает быстрее, но при этом совершенно не имеет средств для поиска ошибок и пошаговой отладки.

Теперь дальше. Переходим к настройкам проекта:

Create Project 6

И в окне:

Create Project 7

Выберите "Все конфигурации". Это нужно для того чтобы не настраивать по отдельности одно и тоже.

Перейдите на "Общие".

В "Выходной каталог" пишите: $(SolutionDir)lib\

Выходной каталог, это та папка, в которой будет лежать наша библиотека. О магической формуле скажу ниже:).

В "Промежуточный каталог" пишите $(SolutionDir)obj\$(Configuration)\

Во время сборки любого проекта создается куча "мусора". Промежуточный каталог, это та папка в которую этот мусор будет записываться. Если вы решите кому-то передать исходники своего проекта, то хорошим тоном будет удаление этой папки. У нас это будет папка obj. Я всегда удаляю ненужное перед тем как выложить исходники.

Вы наверное гадаете, что это за магические $(SolutionDir) и $(Configuration)? Это специальные макросы. Если вы нажмете треугольник справа от опции, то вот что вы увидите:

Create Project 9

Это очень полезный инструмент и советую его изучить. Макрос, это так сказать второе имя. У макроса есть определенное значение. У макроса $(SolutionDir) у меня вот такое значение:

C:\Проекты\уроки по dx11\DirectX 11 шаг за шагом\уроки\1.1\D3D_Framework

Это потому что файл D3D_Framework.sln у меня расположен именно в этой папке. Если я сейчас куда-нибудь перемещу папку D3D_Framework, $(SolutionDir) изменится на соответствующую папку и мне не нужно будет переделывать настройки. При этом у вас $(SolutionDir) скорее всего будет другим, в зависимости от того, где вы создали проект.

Запись $(SolutionDir)lib\ у меня будет развернута в такую:

C:\Проекты\уроки по dx11\DirectX 11 шаг за шагом\уроки\1.1\D3D_Framework\lib\

Очень удобно.

Кроме того, мы должны делать разные библиотеки для debug и release, хотя бы потому что последняя быстрее работает :). Для этого принято к имени debug версии добавлять букву d. Сделаем это так:

Create Project 8

Выберем конфигурацию Debug, потому что в Release мы не должны ничего к имени добавлять.

В поле "Конечное имя" напишем такое $(ProjectName)_d

Теперь, если вы скомпилируете проект в Debug и в Release, то у вас появятся две новые папки - lib и obj. В папке lib будут два файла - D3D_Framework_d.lib и D3D_Framework.lib. Это и есть наша библиотека которую нужно подключать к проекту.

Вот готовый проект на случай, если у вас что-то не получилось: - скачать

Я переименовал папку в D3D_Framework 1 для того чтобы показать что это первая версия фреймворка. Именно это имя я буду учитывать в следующих уроках.

Материалы сообщества доступны в соответствии с условиями лицензии CC-BY-SA , если не указано иное.