Программируем игры на DirectX: различия между версиями
Строка 3: | Строка 3: | ||
== Урок 1 - Создание окна. Инициализация Direct3D == |
== Урок 1 - Создание окна. Инициализация Direct3D == |
||
<h1>Введение</h1> |
|||
Учебник называется "Программируем игры..." потому что мы будет говорить только о программном коде, а темы насчет моделирования, рисования и тд. мы не будем обсуждать. |
|||
Для того чтобы начать читать этот учебник нужно знать хотя бы основы С++ и STL. |
|||
Программировать мы будем с вами для операционной системы Windows XP на С++. В этом учебнике мы охватим такие важные темы как графика, физика, музыка, искусственный интеллект, устройства ввода и по ходу обучения мы создадим пару игр для применения полученных знаний. |
|||
Код используемый в книге был написан мною на Microsoft Visual C++ 6.0 (далее MVC++) с использованием DirectX 9 SDK November 2009 |
|||
<h1>Создание окна. Иницциализация Direct3D</h1> |
|||
Создадим проект с именем Lesson1. Добавим в него файл main.cpp, и начнем его заполнять. |
Создадим проект с именем Lesson1. Добавим в него файл main.cpp, и начнем его заполнять. |
||
Для работы приложения нам потребуется использовать разные библиотеки, их мы сейчас и подключим к нашему проекту. |
Для работы приложения нам потребуется использовать разные библиотеки, их мы сейчас и подключим к нашему проекту. |
Версия от 21:38, 29 ноября 2009
Введение
В этом учебнике я постараюсь научить вас программировать игры на языку С++ с помощью DirectX SDK. Программировать мы будем с вами для операционной системы Windows XP. В этом учебнике мы охватим такие важные темы как графика, музыка, физика, искусственный интеллект. Для применение полученных знаний мы напишем пару игр. Код используемый в книге был написан мною в среде Microsoft Visual C++ 6.0 с использованием DirectX SDK November 2009.
Урок 1 - Создание окна. Инициализация Direct3D
Создадим проект с именем Lesson1. Добавим в него файл main.cpp, и начнем его заполнять. Для работы приложения нам потребуется использовать разные библиотеки, их мы сейчас и подключим к нашему проекту. [code=cpp] //Подключаем библиотеки
- pragma comment(lib,"d3d9.lib")
- pragma comment(lib,"d3dx9.lib")
- pragma comment(lib,"winmm.lib")
[/code]
Дальше мы должны подключить заголовочные файли, чтобы компилятор не выдавал ошибки. [code=cpp] //Подключаем заголовчные файлы
- include <windows.h>
- include <d3d9.h>
- include <d3dx9.h>
[/code]
Объявим глобальные переменные которые мы будем использовать в нашей программе. [code=cpp] //Глобальные переменные HINSTANCE g_hInstance = NULL; //Дескриптор приложения HWND g_hWnd = NULL; //Дескриптор окна int g_iWindowWidth = 800; //Ширина окна int g_iWindowHeight = 600; //Высота окна bool g_bApplicationState = true; //Состояние приложения (true - работает/false - неработает) IDirect3D9 *g_pDirect3D = NULL; //Интерфейс для создания устройства рендеринга IDirect3DDevice9 *g_pDirect3DDevice = NULL; //Интерфейс устройства рендеринга [/code]
Теперь объявим прототипы функций, которые мы с вами напишем. [code=cpp] int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int iCmdShow); //Точка старта приложения long WINAPI WndProc(HWND hWnd,UINT iMsg,WPARAM wParam,LPARAM lParam);//Обработчик сообщений bool InitDirect3D(D3DFORMAT ColorFormat,D3DFORMAT DepthFormat); //Инициализация Direct3D void DrawFrame(); //Рисуем кадр void Shutdown(); //Освобождаем память [/code]
Поговорим о функции WinMain. Эта функция является точкой старта приложения, код который в ней написан начинает выполнение при запуске программы. В этой функции мы должны создать окно и отобразить его. [code=cpp] int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int iCmdShow) {
g_hInstance = GetModuleHandle(NULL);
WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); //Размер структуры wc.style = CS_HREDRAW|CS_VREDRAW; //Стили класса окна wc.lpfnWndProc = WndProc; //Функция обработки сообщений wc.cbClsExtra = 0; //Количество выделяемой памяти при создании приложения wc.cbWndExtra = 0; //Количество выделяемой памяти при создании приложения wc.hInstance = g_hInstance; //Дескриптор приложения wc.hIcon = LoadIcon(NULL,IDI_APPLICATION); //Загружаем стандартную иконку wc.hCursor = LoadCursor(0,IDC_ARROW); //Загружаем стандартный курсор wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);//Окно будет закрашено в белый цвет wc.lpszMenuName = 0; //Не используем меню wc.lpszClassName = "Lesson 1"; //Названия класса wc.hIconSm = LoadIcon(NULL,IDI_APPLICATION); //Загружаем стандартную иконку
if(!RegisterClassEx(&wc)) //Регистрируем класс в Windows { Shutdown(); //Освобаждем память MessageBox(NULL,"Can`t register window class","Error",MB_OK|MB_ICONERROR); //Выводим сообщение return 0; //Завершаем работу приложения }
g_hWnd = CreateWindowEx( //Создаем окно WS_EX_APPWINDOW|WS_EX_WINDOWEDGE, //Расширенный стиль окна "Lesson 1", //Названия класса окна "Lesson 1 - Create Window. Init Direct3D", //Названия окна WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN|WS_CLIPSIBLINGS,//Стиль окна 0, //Позиция окна по оси Х 0, //Позиция окна по оси У g_iWindowWidth, //Ширина окна g_iWindowHeight, //Высота окна NULL, //Это наше главное окно NULL, //Нету меню g_hInstance, //Дескриптор приложения NULL); //Дополнительный настроек не используем
if(g_hWnd == NULL) //Если не создали окно { Shutdown(); MessageBox(NULL,"Can`t create window","Error",MB_OK|MB_ICONERROR);//Выводим сообщение return 0; //Завершаем работу приложения }
if(!InitDirect3D(D3DFMT_R5G6B5,D3DFMT_D16)) //Если не смогли инициализировать Direct3D { Shutdown(); MessageBox(NULL,"Can`t create direct3d","Error",MB_OK|MB_ICONERROR);//Выводим сообщение return 0; //Завершаем работу приложения }
ShowWindow(g_hWnd,SW_SHOW); //Отображаем окно UpdateWindow(g_hWnd); //Обновляем окно SetFocus(g_hWnd); //Устанавливаем фокус на наше окно SetForegroundWindow(g_hWnd); //Устанавливаем приоритет окна выше среднего
MSG msg; ZeroMemory(&msg,sizeof(msg));
while(g_bApplicationState) //Начинаем бесконечный цикл обработки сообщений { if(PeekMessage(&msg,NULL,NULL,NULL,PM_REMOVE))//Получаем сообщения { TranslateMessage(&msg); //Обрабатываем сообщения DispatchMessage(&msg); //Обрабатываем сообщения } else DrawFrame(); //Если сообщений нету рисуем кадры }
Shutdown(); //Освобождаем память return 0; //Завершаем работу приложения
} [/code]
Теперь мы должны написать функцию которая у нас будет обрабатывать сообщения, принцип действия этой функции таков: если мы получили сообщения - перехватываем его, и делаем действия которые нам требуются. [code=cpp] long WINAPI WndProc(HWND hWnd,UINT iMsg,WPARAM wParam,LPARAM lParam) {
switch(iMsg) { case WM_DESTROY: //Если получаем сообщение о разрушении окна { g_bApplicationState = false; //Устанавливаем состояния приложения в false (это значит что цикл обработки сообщений остановиться) return 0; //Говорим виндовс что мы это сообщение обработали } }
return DefWindowProc(hWnd,iMsg,wParam,lParam); //Если нету для нас нужных сообщений, пусть это обрабатывает виндовс
} [/code]
Сейчас наша задача написать функцию инициализации Direct3D. Эта тема для вас новая, постарайтесь сосредоточиться и внимательно просмотреть код. [code=cpp] bool InitDirect3D(D3DFORMAT ColorFormat,D3DFORMAT DepthFormat) {
if((g_pDirect3D = Direct3DCreate9(D3D_SDK_VERSION)) == NULL)//Создаем интерфейс Direct3D return false; //Иначе возвращяем false
D3DPRESENT_PARAMETERS PresParam; //Структура с помощью которой передаем информацию устройству рендеринга при его создании ZeroMemory(&PresParam,sizeof(PresParam)); //Обнуляем
HRESULT hr = NULL; //Создаем переменную для записи в неё результатов работы функций
D3DDISPLAYMODE DisplayMode; //Структура для получения информации о режиме отображения в виндовс hr = g_pDirect3D->GetAdapterDisplayMode( //Получаем режим отображения D3DADAPTER_DEFAULT, //Используем первичную видеокарту &DisplayMode); //Записываем режим отображения в DisplayMode if(FAILED(hr)) //Если не получилось return false; //Возвращяем false
PresParam.hDeviceWindow = g_hWnd; //Дескриптор окна PresParam.Windowed = true; //Оконный режим? PresParam.BackBufferWidth = g_iWindowWidth; //Ширина заднего буфера PresParam.BackBufferHeight = g_iWindowHeight; //Высота заднего буфера PresParam.BackBufferCount = 1; //Количество задних буферов PresParam.EnableAutoDepthStencil = true; //Используем буфер глубины и стенцил буфер PresParam.AutoDepthStencilFormat = DepthFormat; //Формат буфера глубины PresParam.SwapEffect = D3DSWAPEFFECT_FLIP; //Режим смены кадров PresParam.BackBufferFormat = DisplayMode.Format;//Устанавливаем формат пикселя определеный в виндовс
hr = g_pDirect3D->CreateDevice( //Создаем устройство рендеринга D3DADAPTER_DEFAULT, //Используем первичную видеокарту D3DDEVTYPE_HAL, //Устройства рендеринга использует возможности видеокарты g_hWnd, //Дескриптор окна D3DCREATE_HARDWARE_VERTEXPROCESSING, //Обрабатываем вершинны видеокартой &PresParam, //Отдаем параметры устройства &g_pDirect3DDevice); //Создаем устройство рендеринга if(SUCCEEDED(hr)) //Если получилось return true; //Возвращяем true
hr = g_pDirect3D->CreateDevice( //Создаем устройство рендеринга D3DADAPTER_DEFAULT, //Используем первичную видеокарту D3DDEVTYPE_HAL, //Устройства рендеринга использует возможности видеокарты g_hWnd, //Дескриптор окна D3DCREATE_MIXED_VERTEXPROCESSING, //Обрабатываем вершинны смешано (видеокартой и процессором) &PresParam, //Отдаем параметры устройства &g_pDirect3DDevice); //Создаем устройство рендеринга
if(SUCCEEDED(hr)) //Если получилось return true; //Возвращяем true
hr = g_pDirect3D->CreateDevice( //Создаем устройство рендеринга D3DADAPTER_DEFAULT, //Используем первичную видеокарту D3DDEVTYPE_HAL, //Устройства рендеринга использует возможности видеокарты g_hWnd, //Дескриптор окна D3DCREATE_SOFTWARE_VERTEXPROCESSING, //Обрабатываем вершинны процесором &PresParam, //Отдаем параметры устройства &g_pDirect3DDevice); //Создаем устройство рендеринга
if(SUCCEEDED(hr)) //Если получилось return true; //Возвращяем true
return false; //Возвращяем false
} [/code]
Приступим к написанию функции, которая будет рисовать кадры. У устройства рендеринга есть свои недостатки - потеря устройства. потеря устройства возникает например в случаях когда полноэкранное окно не в фокусе и тд. После того как мы потеряли устройство надо его востановить, чтобы дальше можно было отображать сцену. [code=cpp] void DrawFrame() {
HRESULT hr = g_pDirect3DDevice->TestCooperativeLevel();//Проверяем потерял ли Direct3DDevice устройство if(hr == D3DERR_DEVICELOST) //Если да то return; //Выходи из функции g_pDirect3DDevice->Clear( //Очищаем задний буфер 0L, //Размер буфера, 0 - весь буфер NULL, //Область которую будем очищать, NULL - весь буфер D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, //Чистим задний буфер и буфер глубины D3DCOLOR_XRGB(0,0,0), //Цвет в который очищаем задний буфер, в нашем случае черный 1.0f, //Очищаем буфер глубины, заполнив его единицами 0L); //Этот параметрт игнорируеться так как не выстовлен соответсвующий флаг
g_pDirect3DDevice->BeginScene(); //Начало сцены g_pDirect3DDevice->EndScene(); //Конец сцены g_pDirect3DDevice->Present(NULL,NULL,NULL,NULL);//Отображаем весь задний буфер
} [/code]
Ну и последняя функция, которая будет освобождать выдуленеую память. Освобождать память надо в обратном порядке выделения памяти. [code=cpp] void Shutdown() {
if(g_pDirect3DDevice != NULL) //Если мы еще не освободили интерфейс рендеринга { g_pDirect3DDevice->Release(); //То освобождаем его g_pDirect3DDevice = NULL; //И устанавливаем в ноль }
if(g_pDirect3D != NULL) //Если мы еще не освободили интерфейс d3d { g_pDirect3D->Release(); //То освобождаем его g_pDirect3D = NULL; //И устанавливаем в ноль }
if(!DestroyWindow(g_hWnd)) //Если не получилось разрушить окно g_hWnd = NULL; //Устанавливаем дескриптор окна в ноль
if(!UnregisterClass("Lesson 1",g_hInstance)) //Если не получилось удалить наше зарегестрированое окно g_hInstance = NULL; //Устанавливаем дескриптор приложения в ноль
} [/code]
Теперь компилируем и смотрим что получилось) Мы написали приложение которое инициализирует Direct3D в оконном режиме, а в полоэкранном режиме мы напишем когда наше приложение будет поддерживать клавиатуру.