Пишем свой игровой движок.Часть 1 — WinApi и Directx
Всем привет!
Давно хотели создать свой движок, но не знаете как?
Я вам расскажу!
Итак, нам надо установить Visual studio 2022 с Windows SDK.
компоненты
Вот следущее нам надо установить и дополнительно языковой пакет 'Английский'
Все установили? Теперь погнали к коду!
Создаем на С++ пустой проект
И называем Beta-Engine
Сначала надо зайти в настройки и делаем так:
И создаем файл Main.cpp
Для тех кто не знает как создать
Нажимаем на папку пкм и 'Create new item'
И приступаем к коду!
Я буду обозначать концы функций как
//winapi functon
И Т.Д.
Нам надо сначала включить в проект файлы WinApi
#include
Отлично написали.
теперь нам надо Создать точку входа и сразу делаем дескриптор окна
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
//win api start
//дескриптор окна
HWND hWnd;
//этот калсс содержит информацию о нашем окне
WNDCLASSEX wc;
//win api end
}
Теперь нам надо же заполнить информацию об окне?
Ну так поехали:
//очищаем память для окна
ZeroMemory(&wc, sizeof(WNDCLASSEX));
//заполняем информацию
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW; //стиль окна
wc.lpfnWndProc = WindowProc; //функция обработки сообщений
wc.hInstance = hInstance; // у меня нет коментариев
wc.hCursor = LoadCursor(NULL, IDC_ARROW); //стандартный курсор
wc.hbrBackground = (HBRUSH)COLOR_WINDOW; //задний фон
wc.lpszClassName = L"WindowClass1"; //название класса
//win api end
Пока вы пишете это я расскажу прикол:
Я сижу на ПК пишу этот пост и на телефоне вылезает реклама, я мышкой попытался 'закрыть рекламу на телефоне'
Пишем дальше:
Чтобы создать окно надо же его зарегистрировать? Конечно!
//регистрируем окно
RegisterClassEx(&wc);
И создаем окно наконец-то
//создаем окно
hWnd = CreateWindowEx(NULL,
L"WindowClass1", //имя класса
L"Our First Windowed Program", // название окна
WS_OVERLAPPEDWINDOW, //стиль окна
300, // x-позиция
300, // y-позиция
500, // ширина окна
400, // высота окна
NULL, // у нас нет родительских окон,значит Null
NULL, // мы не используем меню, NULL
hInstance, // Дескриптор приложения
NULL); // используем только когда мульти-оконость, NULL
//win api end
Теперь показываем окно (кстати во всем коде много раз используется наш hwnd и wc):
ShowWindow(hWnd, nCmdShow);
Создаем обработку сообщений
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
//переводим сообщение в правильный формат
TranslateMessage(&msg);
//отправляем сообщения в windproc
DispatchMessage(&msg);
}
return msg.wParam;
//win api закончился
СПОЙЛЕР: весь код для тех кто запутался
#include
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
//win api start
//дескриптор окна
HWND hWnd;
//этот калсс содержит информацию о нашем окне
WNDCLASSEX wc;
//очищаем память для окна
ZeroMemory(&wc, sizeof(WNDCLASSEX));
//заполняем информацию
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW; //стиль окна
wc.lpfnWndProc = WindowProc; //функция обработки сообщений
wc.hInstance = hInstance; // у меня нет коментариев
wc.hCursor = LoadCursor(NULL, IDC_ARROW); //стандартный курсор
wc.hbrBackground = (HBRUSH)COLOR_WINDOW; //задний фон
wc.lpszClassName = L"WindowClass1"; //название класса
//регистрируем окно
RegisterClassEx(&wc);
//создаем окно
hWnd = CreateWindowEx(NULL,
L"WindowClass1", //имя класса
L"Our First Windowed Program", // название окна
WS_OVERLAPPEDWINDOW, //стиль окна
300, // x-позиция
300, // y-позиция
500, // ширина окна
400, // высота окна
NULL, // у нас нет родительских окон,значит Null
NULL, // мы не используем меню, NULL
hInstance, // Дескриптор приложения
NULL); // используем только когда мульти-оконость, NULL
ShowWindow(hWnd, nCmdShow);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
//переводим сообщение в правильный формат
TranslateMessage(&msg);
//отправляем сообщения в windproc
DispatchMessage(&msg);
}
return msg.wParam;
//win api закончился
}
Чтобы закончить обработку сообщений нам надо написать windowproc
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// сортируем сообщения
switch(message)
{
// сообщения когда закрывают окно
case WM_DESTROY:
{
// закрываем окно
PostQuitMessage(0);
return 0;
} break;
}
// Обработка всех сообщений, не полученных инструкцией switch
return DefWindowProc (hWnd, message, wParam, lParam);
}
Теперь в начало кода надо сделать прототип windowproc
LRESULT CALLBACK WindowProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam);
Вот куда ставить:
вот весь код:
Весь код
#include
LRESULT CALLBACK WindowProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
//win api start
//дескриптор окна
HWND hWnd;
//этот калсс содержит информацию о нашем окне
WNDCLASSEX wc;
//очищаем память для окна
ZeroMemory(&wc, sizeof(WNDCLASSEX));
//заполняем информацию
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW; //стиль окна
wc.lpfnWndProc = WindowProc; //функция обработки сообщений
wc.hInstance = hInstance; // у меня нет коментариев
wc.hCursor = LoadCursor(NULL, IDC_ARROW); //стандартный курсор
wc.hbrBackground = (HBRUSH)COLOR_WINDOW; //задний фон
wc.lpszClassName = L"WindowClass1"; //название класса
//регистрируем окно
RegisterClassEx(&wc);
//создаем окно
hWnd = CreateWindowEx(NULL,
L"WindowClass1", //имя класса
L"Our First Windowed Program", // название окна
WS_OVERLAPPEDWINDOW, //стиль окна
300, // x-позиция
300, // y-позиция
500, // ширина окна
400, // высота окна
NULL, // у нас нет родительских окон,значит Null
NULL, // мы не используем меню, NULL
hInstance, // Дескриптор приложения
NULL); // используем только когда мульти-оконость, NULL
ShowWindow(hWnd, nCmdShow);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
//переводим сообщение в правильный формат
TranslateMessage(&msg);
//отправляем сообщения в windproc
DispatchMessage(&msg);
}
return msg.wParam;
//win api закончился
}
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// сортируем сообщения
switch (message)
{
// сообщения когда закрывают окно
case WM_DESTROY:
{
// закрываем окно
PostQuitMessage(0);
return 0;
} break;
}
// Обработка всех сообщений, не полученных инструкцией switch
return DefWindowProc(hWnd, message, wParam, lParam);
}
Запускаем и видим:
Пока что нам надо добавить сам d3d
#include //добавляем в начало
#pragma comment (lib, "d3d11.lib")
теперь объявляем:
IDXGISwapChain *swapchain; // Указатель на интерфейс цепочки буферов
ID3D11Device *dev; // указатель на интерфейс устройства Direct3D
ID3D11DeviceContext *devcon; // указатель на контекст устройства Direct3D
объявляем функции (позже мы их напишем)
void d3dinit(HWND hWnd);
void d3dcl(void);
после show window пишем:
d3dinit(hWnd)
вот так примерно:
и также перед 'return msg.wparam'
уже создаем функцию d3dinit в самом конце
void d3dinit(HWND hWnd)
{
//создаем структуру которая держит информацию о буффере
DXGI_SWAP_CHAIN_DESC scd;
//очищаем память для использования
ZeroMemory(&scd, sizeof(DXGI_SWAP_CHAIN_DESC));
scd.BufferCount = 1; //Один задний буфер
scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // используем 32 битный цвет
scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; //Как следует использовать цепочку буферов
scd.OutputWindow = hWnd; // Окно, которое будет использоваться
scd.SampleDesc.Count = 4; // кол-во уровней сглаживания
scd.Windowed = TRUE; // оконный/полноэкранный режим
//создаем девайс d3d
D3D11CreateDeviceAndSwapChain(NULL,
D3D_DRIVER_TYPE_HARDWARE,
NULL,
NULL,
NULL,
NULL,
D3D11_SDK_VERSION,
&scd,
&swapchain,
&dev,
NULL,
&devcon);
}
Написали? Молодцы
Теперь следущую функцию d3dcl
void d3dcl(void)
{
// закройте и освободите все существующие COM-объекты
swapchain->Release();
dev->Release();
devcon->Release();
}
теперь запускаем:
Как и в прошлый раз
Всем пока! В следущей части сделаем отрисовку кадров.