Только что пришла в голову мысль — нужно найти какое-то хобби. Иначе с катушек можно съехать. А поскольку я весьма бесполезный человек, ничего кроме как тыкать кнопки не умеющий, хобби будет такое: не реже раза в неделю устраивать стрим с написанием игрушки. После стрима запись публикуется на Хабре. (Можно попробовать постить на Хабр прям лайв, но это сильно сложней).
Написание очень feedback driven — если кому-то нужны пояснения, то я могу пояснить как смогу. Если есть предложения — постараюсь учитывать. На хабре читаются все комментарии до последнего, в других местах — как получится.
Первый блин комом вот здесь:
Под катом — тезисное описание для тех, кому справедливо влом тратить на просмотр полтора часа.
Направляющие идеи
Первый интересный аспект в том, что хочется написать всё без хрюмворков. Как обычный джавист делает? Возникла проблема — фигачишь в зависимости хрюмворк, и он тебе решает вопрос. Цена этому — из-за подобного свинства проект превращается в свинарник, и никто не может в точности определить, что же происходит. Хочется попробовать написать без этой свинофермы, чистенько и аккуратно. Возможно, это невозможно — пожалуйста, сообщите в комментариях.
Второй аспет. Я всю жизнь кодил на Java и PHP. Был опыт разработки игрушек, но только серверной части с ответом по сети, и ни разу — настоящего десктопного приложения на C++. Поэтому, по сути это будет шоу уродов — человек не разбирающийся в вопросе попробует как-то своими словами описать, что происходит.
Нет, никакого практического значения всё это не имеет. Это чтобы не съехать с катушек, и чтобы мотивировать взрослых бородатых дяденек тоже взяться за клавиатуру и запилить какую-нибудь игрушку.
Рутина наносит удар в пятак
К сожалению, программирование никогда не начинается с легкого и приятного развлечения. Вначале происходит период болезненного копания в настройках среды и IDE.
Первое что я понял относительно Visual Studio — это отличный инструмент для профессионала, но на новичка он производит очень фрустрирующее впечатление. После Eclipse и IntelliJ IDEA всё, знаете ли, какое-то неестественное, сиреневенький бесперспективняк с переподвыподвертом. Дело не в коем случае не в VS, дело во мне :-)
И с плагинами постоянно какая-то беда.
Поэтому волевым решением отказываемся от Вижуалки и смотрим, что ещё есть. Vim, Emacs и прочие редакторы, не понимающие структуры исходника — отправляются лесом. Первая же попытка использовать Eclipse провалилась, поэтому инструментом был выбран CLion.
Проблема с CLion в том, что он не всё ещё не умеет полноценно работать с компилятором Visual Studio. Если попробовать сделать что-нибудь в нём, то будет вот такое:
Конечно, тут бы в тред набежать гошникам и начать кричать надрывным голосом, что отладочных принтов хватит всех. Но мы их слушать, конечно не станем.
Поэтому нужно то, что CLion умеет хорошо, и это MinGW.
Выбираем MinGW
Есть большая таблица версий.
И по ней видно, что имеет смысл смотреть только на Cygwin и Msys2. Msys2 какой-то странноватый — у него мало коммитеров, пакеты лежат на личном гитхабе одного из авторов. С другой стороны, он действительно клёвый — там свежий компилятор и куча пакетов. Это имеет решающее значение.
Установка заключается в прокликивании next-next-ok, и потом многократном выполнении команды обновления (pacman -Syu
, как в Арчлинуксе) с перезапуском терминала Msys2, пока он не скажет, что всё получилось в лучшем виде.
Если просто установить Msys2 по инструкции и попробовать добавить его в CLion, не будет найдено ни одного тулчейна. Его нужно поставить самостоятельно, это делается командой pacman -S mingw-w64-x86_64-toolchain
.
После установки этого пакета, CLion запускает helloworld (который генерится автоматически при создании нового проекта) и в нём работает отладка.
Проверка возможности #1: простое окно
Во-первых, хочется понять, запустится ли вообще хоть какое-то десктопное приложение. Всё-таки, это не родной PlatformSDK, а MinGW.
Для этого подойдет незамысловатая прога, рисующая окно с помощью винапи.
И да, с MinGW она запускается. А вот с тулчейном Visual Studio оно сыпет какими-то ошибками, но с ними разбираться я не стал, потому что — а зачем, если у нас уже работает целевая платформа?
Проверка возможности #2: треугольник с шейдером
Понятно, что для написания чего-то жизнеспособного недостаточно выводить пиксели на окне. Это тормозно, неудобно, нет никаких модных штучек. Короче, нам нужен DirectX.
Ключевой вопрос про CLion+MinGW — именно это. Если они не смогут юзать DirectX, то отправляются в помойку.
Для тестирования был нагуглен очень короткий туториал, состоящий всего из двух файлов: в одном весь код на C++, в другом — шейдер. Задача в том, чтобы запинать его работать.
Кроме однофайловости этот туториал очень хорош тем, что там на пальцах рассказывается, как он работает. Возможно, эту статью стоит перевсти на Хабр (напишите в комментариях).
Готовый результат лежит вот здесь GitHub. Здесь опишу, какие проблемы встретились.
Мелочи и мусор
Много L-строк стали просто строками. Можно поудалять буковки L. Во многих местах нужно заменить NULL на 0 чтобы не падало.
Несмотря на то, что использовался set(CMAKE_CXX_STANDARD 17)
, этом плане ничего интересного не произошло вообще, ничего не развалилось.
Заголовки и либы
Дело, конечно, в том, что этот пример — очень древний, написанный во времена задолго до Windows 10. DirectX теперь не находится в отдельном DirectX SDK как во времена нашей неоднозначной молодости, а засунут прямо в Windows SDK.
Поэтому первое что нужно сделать — запустить установщик Visual Studio и проверить, что установлена свежая версия Windows SDK.
Второй вопрос — в заголовках.
#include <d3d11.h>
#include <d3dx11.h>
#include <d3dx10.h>
Их больше нет, нужно что-то вроде:
#include <d3d9.h>
#include <d3d10.h>
#include <d3d11.h>
#include <dxgi.h>
И потом в CMakeLists добавить поиск до них в конец файла:
set(LIBS d3d9 d3d11 d3dcompiler_43)
target_link_libraries(src ${LIBS})
Отсутствующие API
Раньше была вот такая структура:
typedef struct D3DXCOLOR {
FLOAT r;
FLOAT g;
FLOAT b;
FLOAT a;
} D3DXCOLOR, *LPD3DXCOLOR;
И больше её нет. Впрочем, во всех местах, где она реально была нужна, получилось заменить D3DXCOLOR(0.0f, 0.2f, 0.4f, 1.0f)
на {0.0f, 0.2f, 0.4f, 1.0f}
.
Проблема интересней оказалась с D3DX11CompileFromFile
. Её больше нет!
В статье Living without D3DX её предложили заменить на D3DCompileFromFile
.
Но вот проблемка, D3DCompileFromFile
у нас тоже почему-то недоступно!
Небольшое расследование показало, что документация Microsoft предлагает воспользоваться очень новой версией API:
А MinGW отправляет нас в прошлое на несколько лет:
Впрочем, в 32-битной версии есть более новый заголовок, но как его присобачить к 64-битам я не разобрался. 32 бита на, наверное, на фиг не сдались.
С одной стороны, это очень печально, потому что предвещает гемор в отношениях с MinGW в дальнейшем. Интересно, кто мантейнеры всех этих дел.
С другой стороны, если взять отсутствующую D3DCompile
и присутствующую D3DCompileFromFile
:
HRESULT WINAPI D3DCompileFromFile(
in LPCWSTR pFileName,
in_opt const D3D_SHADER_MACRO pDefines,
in_opt ID3DInclude pInclude,
in LPCSTR pEntrypoint,
in LPCSTR pTarget,
in UINT Flags1,
in UINT Flags2,
out ID3DBlob ppCode,
out_opt ID3DBlob ppErrorMsgs
);
HRESULT WINAPI D3DCompile(
in LPCVOID pSrcData,
in SIZE_T SrcDataSize,
in_opt LPCSTR pSourceName,
in_opt const D3D_SHADER_MACRO pDefines,
in_opt ID3DInclude pInclude,
in_opt LPCSTR pEntrypoint,
in LPCSTR pTarget,
in UINT Flags1,
in UINT Flags2,
out ID3DBlob ppCode,
out_opt ID3DBlob ppErrorMsgs
);
то окажется, что там разница только в следующем:
in LPCWSTR pFileName,
против
in LPCVOID pSrcData,
in SIZE_T SrcDataSize,
in_opt LPCSTR pSourceName,
К сожалению, я не знаю C++, поэтому наговнокодил как умел: просто добавил вычитывание файла и редирект этих данных в D3DCompile
.
void WINAPI D3DCompileFromFile(const char *filename,
const D3D_SHADER_MACRO *defines, ID3DInclude *include, const char *entrypoint,
const char *target, UINT sflags, UINT eflags, ID3DBlob **shader, ID3DBlob **error_messages) {
SIZE_T data_size;
char* buffer;
ifstream infile;
infile.open(filename, ios::binary);
infile.seekg(0, ios::end);
data_size = infile.tellg();
infile.seekg(0, ios::beg);
buffer = new char[data_size];
infile.read(buffer, data_size);
infile.close();
D3DCompile(buffer, data_size,filename,
defines, include,entrypoint,
target, sflags, eflags, shader, error_messages);
}
Единственное важное различие между почившим в тьме веков D3DX11CompileFromFile
и нашим самопальным D3DCompileFromFile
— в отсутствии в новом API ID3DX11ThreadPump
в качестве параметра. Это что-то для асинхонности, возможно какой-то тредпул? Впрочем, в туториале он и не использовался, там на его месте стоит 0.
Итоги
Связка DirectX + MinGW + Msys2 + CLion является достаточно жизнеспособной, чтобы запилить простую игру. Есть возможность не только использовать базовое винапи, но и рисовать, и даже с шейдерами.
В общем-то, всё. Напоминаю, что нехитрый результат лежит на GitHub.
Пожалуйста, помните, что это не мой код, а модифицированный текст из туториала — впрочем я надеюсь, что такое его использование является честным и целевым. Но тем не менее, именно поэтому там нет никой нормальной лицензии.
Фидбек
Пожалуйста, пишите в комментариях ваши замечания и предложения. Ради этого всё и делается, послушать что вы скажете. Особенно если вы — матёрый игропрограммист, прямо зубр, бизон, и не помещаешься в тред по размерам. Обязательно найдо зайти и что-нибудь написать.
Напоминаю, что я, как и любой дргой блоггер, питаюсь лайками и дизайками. Чем яростней вы будете наяривать стрелку вверх под этим постом, тем более вероятно выйдет следующий пост и стрим.
олег родился в интернете
в почтенной геймерской семье
питался лайками и в гугол
за двойки ставили его
© Александр Раевский
Автор: olegchir