Пишем свой игровой движок.Часть 1 — WinApi и Directx

Всем привет!

Давно хотели создать свой движок, но не знаете как?

Я вам расскажу!

Итак, нам надо установить Visual studio 2022 с Windows SDK.

компоненты

компоненты

Вот следущее нам надо установить и дополнительно языковой пакет 'Английский'

89372834f0c772af9f75aabba4d50573.png

Все установили? Теперь погнали к коду!

Создаем на С++ пустой проект

f0905829fdb5e70778f05b8b774c7f18.png

И называем Beta-Engine

Сначала надо зайти в настройки и делаем так:

cac1f6c43319041dc03291998c7894b1.png

И создаем файл Main.cpp

Для тех кто не знает как создать

8016ec378fabfdf424f4cbbbc01003e3.png

Нажимаем на папку пкм и '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);

Вот куда ставить:

be5c9960ff713a949e8b6f411d9ffd52.png

вот весь код:

Весь код

#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);
}

Запускаем и видим:

5042ccb8f3fe82136d3a8f0a83523d9e.png

Пока что нам надо добавить сам 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)

вот так примерно:

1329f56b952cd257236789bcab6b9383.png

и также перед 'return msg.wparam'

380315d940b4bf9289012984d5ce64ab.png

уже создаем функцию 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();
}

теперь запускаем:

Как и в прошлый раз

Всем пока! В следущей части сделаем отрисовку кадров.

© Habrahabr.ru