Только что пришла в голову мысль — нужно найти какое-то хобби. Иначе с катушек можно съехать. А поскольку я весьма бесполезный человек, ничего кроме как тыкать кнопки не умеющий, хобби будет такое: не реже раза в неделю устраивать стрим с написанием игрушки. После стрима запись публикуется на Хабре. (Можно попробовать постить на Хабр прям лайв, но это сильно сложней).
Написание очень 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.
Пожалуйста, помните, что это не мой код, а модифицированный текст из туториала — впрочем я надеюсь, что такое его использование является честным и целевым. Но тем не менее, именно поэтому там нет никой нормальной лицензии.
Фидбек
Пожалуйста, пишите в комментариях ваши замечания и предложения. Ради этого всё и делается, послушать что вы скажете. Особенно если вы — матёрый игропрограммист, прямо зубр, бизон, и не помещаешься в тред по размерам. Обязательно найдо зайти и что-нибудь написать.
Напоминаю, что я, как и любой дргой блоггер, питаюсь лайками и дизайками. Чем яростней вы будете наяривать стрелку вверх под этим постом, тем более вероятно выйдет следующий пост и стрим.
олег родился в интернете
в почтенной геймерской семье
питался лайками и в гугол
за двойки ставили его
© Александр Раевский
Комментарии (14)
JeriX
01.10.2018 11:20+1Спасибо за пост!
У меня вопрос немного сбоку: а чем был обоснован выбор языка для разработки без «хрюмворков»?
// отличное и ёмкое слово, кстати :)
Почему именно C++? И почему не Rust?olegchir Автор
01.10.2018 11:44Вдохновляюсь крутейшей серией видеокастов Handmade Hero, хочется сделать как у него, но по-другому.
Возможно, как раз решив часть проблем с помощью Rust или Java. На Java скорей всего будет скриптовая система, JVM можно ембедить в крестовое приложение. Если будут идеи, зачем можно использовать Rust — напиши! Может, тоже скрипты?
В любом случае, чтобы избавляться от проблем — нужно их вначале нажить :-)
А еще просто хочется разобраться в C++. Зачем? Потому что моя специализация — Java. Чтобы изменять виртуальную машину Java — нужно понимание C++. Сейчас я исправляю баги в C++ коде, и это получается ОК совершенно без понимания. Чтобы его достичь, вероятно, нужно написать какое-нибудь приложение с нуля.
JeriX
01.10.2018 11:52+1Встречный вопрос: а почему хочется разобраться именно с C++? Чтобы научиться понимать код разных JVM? Или просто хочется что-нибудь нативного?
Просто Rust тоже нативный, и хоть и гораздо сложнее в освоении в самом начале, зато проще после первого месяца-двух, а C++ для меня как был переусложнённым, так и осталсяolegchir Автор
01.10.2018 12:15Не понимать код разных JVM, а разрабатывать эти JVM. Там легаси 20-летней давности. Плюс совершенно не на всех платформах есть компилятор Rust — там свежий С++ то не всегда есть, и нужен стандарт 98-ого года.
Зачем это нужно на практике. Например, последнюю пару недель мы с друганом сражаемся с попыткой спортировать OpenJDK на новую процессорную архитектуру. Есть куча постов о кишках JVM (пример раз, пример два), доклады по GraalVM, итп. Чтобы их писать, нужно понимать что пишешь, рыться в коде. В принципе, большинство задач не требует именно написания чего-либо — достаточно понимать и мочь править баги "по смыслу". Но и общее ощущение составить хочется.
Я знаю про Rust, это крутая штука, просто вряд ли он подходит именно мне. Если кто-то захочет сделать выделенный подкаст про Rust, это было бы неплохо!
budda
01.10.2018 17:43+2Если подкаст для начинающих и непрофессионалов, то почему не учли лицензию IDE :(
Visual Studio Community бесплатный продукт, а в CLion нет Community версии, как для Java & Python, только 30 дневный trial. Поэтому интересует, что будет с CLion после 30 дней? Будут выдаваться сообщения, ограничения рабочей сессии или какие то другие прелести?olegchir Автор
01.10.2018 22:02Нужно будет отдельно объяснить, как билдить через другие IDE. То что это будет возможно — гарантируется тем, что для сборки используется CMake, не что-то IDE-специфичное. Можно писать хоть в блокноте и собирать из консоли. Я сделаю.
CLion сильно лучше для обучения. Особенно для тех, кто раньше писал на Java, PHP, Python, Ruby, .NET и других продуктах, где в качестве IDE лидируют продукты JetBrains. Если я ещё и IDE поменяю на VS, то буду чувствовать себя совсем униженным и сбитым с толку. В смысле, ну вот пишешь ты в блокноте или VSCode — будет то же самое, что в CLion, но без подсказок, поиска, рефакторингов, и так далее.
Когда закончится CLion, нужно будет скачать следующий EAP. Скорей всего, EAP ещё долго будут бесплатными. Кроме того, для студентов он вообще всегда бесплатный.
Для профессионалов Jetbrains Toolbox — вещь незаменимая. У меня он куплен на свои личные деньги, не работодателя. Окупается. Но этот момент дальше раскрывать не стану, чтобы кто-то не подумал, что это маркетинг JB.
asm0dey
01.10.2018 20:37Спасибо! До сих пор для меня плюсы были совсем магией, а вот ты их приделал к чему-то, что я даже енмножко понимаю. Чуть-чуть, но понимаю.
olegchir Автор
01.10.2018 21:52Ты бы знал, как в CLion проще для человека, который последние десять лет писал в Идее. Вещи находятся на привычных местах и работают привычным способом. Ну да, язык другой, зато всё остальное — понятное. Когда непонятны ни язык, ни среда, совсем нехорошо становится.
asm0dey
01.10.2018 22:35+1Ну я когда-то пользовался Visual Studio, она мне тоже больмень понятна, но для плюсов там всё совсем неудобно, намного хуже чем для шарпа.
Serge78rus
olegchir Автор
Additional software in package manager, GCC version.
От Msys2 ощущения как от работы с полноценным GNU/Linux: заходишь в терминал, и у тебя есть всё что душа пожелает. Самых свежих версий, включая GCC. Если версии несвежие или собранные не с теми флагами — есть внятное описание, как быстро забежать в PKGBUILD и пересобрать новое (это та же система, что в ArchLinux). Это просто космос, в отличие от красноглазия, которое начинается каждый раз в Windows, когда ты пытаешься что-то собрать так как тебе надо из исходников.
Serge78rus
С Msys2 понятно, неясно в чем, согласно приведенной таблицы, преимущества Cygwin перед MinGW.
olegchir Автор
Так вся таблица — это mingw, непонятен вопрос
Ты имеешь в виду, чем он лучше, чем mingw, который идет отдельно? Ну тут к тебе вопрос: у него есть ли пакетный менеджер с репозиторием готовых бинарных пакетов? У cygwin есть, хоть там все и несколько по-некрофильски.
Еще одна эксклюзивная фишка cygwin: он прочно обосновался во всевозможных туториалах по сборке чего-нибудь под Windows: там фигурируют вполне конкретные сигвино-специфичные названия пакетов, под него заточены сборочные скрипты. Например, OpenJDK при сборке из исходников под Windows почему-то сыпал странными ошибками, а в Cygwin работал норм. Если не ошибаюсь, они даже в мейке его поддержали, и когда падает сборка из-за недостатка заголовков, то советы какой пакет установить есть не только для Ubuntu и Fedora, но и для Cygwin
playermet