Намагаємося налаштувати середовище для розробки гри під Windows
Тільки що прийшла в голову думка — потрібно знайти якесь хобі. Інакше з глузду можна з’їхати. А оскільки я дуже марний людина, нічого крім як тикати кнопки не вміє, хобі буде таке: не рідше одного разу на тиждень влаштовувати стрім з написанням іграшки. Після стріму запис публікується на Хабре. (Можна спробувати постити на Хабр прям лайв, але це значно складніше).
Написання дуже 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.0 f, 0.2 f, 0.4 f, 1.0 f)
на {0.0 f, 0.2 f, 0.4 f, 1.0 f}
.
Проблема цікавішою виявилася з 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.
Будь ласка, пам’ятайте, що це не мій код, а модифікований текст з туториала — втім я сподіваюся, що таке його використання є чесним та цільовим. Але тим не менш, саме тому там немає нікою нормальної ліцензії.
Фідбек
Будь ласка, пишіть в коментарях ваші зауваження та пропозиції. Заради цього все і робиться, послухати, що скажете ви. Особливо якщо ви — досвідчений игропрограммист, прямо зубр, бізон, і не поміщаєшся в тред за розмірами. Обов’язково найдо зайти і що-небудь написати.
Нагадую, що я, як і будь-другого блогер, харчуюся лайками і дизайками. Чим лютіше ви будете телефонувати стрілку вгору під цим постом, тим більш імовірно вийде наступний пост і стрім.
олег народився в інтернеті
в поважній геймерської сім'ї
харчувався лайками і в гугол
за двійки ставили його
© Олександр Раєвський