GameDev :: OpenGL @ Урок 10

Урок 10. Каркас на WinAPI. Часть 1:

Сегодня у нас будет очень важный урок. В первых уроках мы создавали с вами каркас для наших приложений. Но тот каркас был очень “детским”. Его использовать не можно в более-менее профессиональной работе. Нам нужен новый каркас, написанный с использованием WinAPI. Как вы уже догадались, сегодня мы начнем (подчеркиваю, начнем) писать каркас для всех наших последующих приложений. Почему начнем? Для того чтобы было легче понять, я разобью наш каркас на пару уроков. Просто тема эта не очень простая, и я думаю, что давать все сразу неразумно.
Итак, давайте приступим к работе. В этом уроке мы напишем приложение, которое просто создает пустое окошко. А в следующем уроке мы это окошко свяжем с OpenGL. В прошлом уроке мы чуть затронули тему WinAPI, сегодня мы продолжим ее.
Создайте Win32 Application, как в прошлом уроке, и добавьте *.cpp файл. Вначале это файла подключим *.h файл для работы с WinAPI (windows.h) и опишем глобальные переменные, а также функции, которые нам понадобятся:


#include <windows.h>
int InitGL(HWND hWndMain);
// Здесь мы будем инициализировать OpenGL.
void ReleaseAll(); // Эта функция нужна для корректного завершения программы.
void DrawScene(); // В этой функции будем рисовать сцену
int MessProc();
// Обработка сообщений.
HWND CreateMainWindow(LPCSTR pWindowName, int nWidth, int nHeight, int nCmdShow=SW_NORMAL, bool bIsOpenGL=false);
//Функция для создания окна.
HINSTANCE hInstance;
// эти две переменные мы разбирали в прошлом уроке.
HWND hWndMain;

Теперь давайте я вам немного обьясню. Первые три функции понятны, а вот зачем нужна MessProc()? Дело в том, что в Windows’е все построено на сообщениях. То есть, операционная система получает какое-то сообщение от любого устройства, или программы, а затем обрабатывает его. Приведу один пример. Когда вы нажимаете в какой-нибудь программе на кнопку “Закрыть”, то программа генерирует сообщение WM_QUIT. Затем, это сообщение становится в очередь (ведь ОС обрабатывает сотни сообщений). Когда подходит очередь этого сообщения, Windows его читает, а затем закрывает окно. Так вот, наше приложение должно уметь корректно работать с сообщениями. Для этого мы и пишем эту функцию. Функцию CreateMainWindow мы рассмотрим ниже.
Поехали дальше. А дальше мы создаем главную функцию, с которой начинается выполнение программы. Затем описываем функции ReleaseAll(), DrawScene(), InitGL():

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
hWndMain = CreateMainWindow("WinAPI Window",640,480);
if(!hWndMain)
return 0;
if(!InitGL(hWndMain))
return 0;
int nRet = MessProc();
ReleaseAll();
return nRet;
}

int InitGL(HWND hWndMain)
{
// Работу с OpenGL мы разберем в следующем уроке, так что пока тут пусто.
return 1;
}

void ReleaseAll()
{
// В этом уроке обойдемся просто называнием функции :)
}

void DrawScene()
{
// Здесь мы будем рисовать нашу сцену.
}

Здесь все понятно, кроме функции WinMain(). Вот ее мы и рассмотрим. Вначале пытаемся создать главное окно, если не получилось, то возвращаем 0 (и программа завершается). Желательно еще выдавать сообщения об ошибках, что бы вы (пользователи вашей программы) могли узнать, в чем проблема. Сообщениями об ошибках мы займемся в следующем уроке, пока это не очень важно. Следующей функцией мы инициализируем систему OpenGL и запускаем цикл обработки сообщений. И в конце завершаем корректно работу приложения. Остальные функции пока понятны.
Дальше мы напишем функцию обработки сообщений главного окна нашего приложения:


LRESULT WINAPI MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch( msg )
{
case WM_LBUTTONDOWN:
break;
case WM_SIZE:
break;
case WM_DESTROY:
PostQuitMessage( 0 );
return 0;
}
return CallWindowProc( (WNDPROC)DefWindowProc, hWnd, msg, wParam, lParam );
}

В этой функции мы перебираем разные сообщения. При закрытии окна мы посылаем в очередь сообщение WM_QUIT с помощью функции PostQuitMessage(0).
Теперь мы должны создать главное окно. Сделаем это следующим образом:


HWND CreateMainWindow(LPCSTR pWindowName, int nWidth, int nHeight, int nCmdShow, bool bIsOpenGL)
{
WNDCLASS wndclass;

wndclass.style = 0;
wndclass.lpfnWndProc = (WNDPROC)MainWndProc;
wndclass.cbClsExtra = 0;
// нет дополнительных данных об окне
wndclass.cbWndExtra = 0;
// тоже самое
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon (hInstance, "WinAPI");
// Загрузка иконки по умолчанию
wndclass.hCursor = LoadCursor (NULL,IDC_ARROW);
// Загружаем указатель мышки- стрелку
wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW);
// Цвет фона, в дальнейшем будет не нужен
wndclass.lpszMenuName = NULL;
// Нам не нужно меню
wndclass.lpszClassName = "WinAPI";
// Название класса

if(!RegisterClass(&wndclass))
return 0;

DWORD dwStyle = WS_OVERLAPPEDWINDOW;
if(bIsOpenGL)
dwStyle |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN;

HWND hWnd = CreateWindow("WinAPI", pWindowName, dwStyle,
(GetSystemMetrics(SM_CXSCREEN)-nWidth)/2,
(GetSystemMetrics(SM_CYSCREEN)-nHeight)/2,
nWidth, nHeight,
0, 0, hInstance, NULL);
if(hWnd)
{
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
}
return hWnd;
}

Что бы создать само окно, нам нужно вначале зарегистрировать класс этого окна. WNDCLASS- это структура, которая содержит ряд полей. Рассмотрим несколько полей:
lpfnWndProc – указываем функцию, которая является обработчиком главного окна.
hInstance - уникальный идентификатор приложения, автоматически передается системой в качестве параметра функции WinMain().
Когда мы заполнили все поля структуры, пытаемся зарегистрировать наш класс. Если все успешно, тогда создаем само окошко. И затем рисуем его.
И в конце программы мы опишем наш обработчик сообщений:

int MessProc()
{
MSG msg={0};
while(msg.message!=WM_QUIT)
{
if(PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
{
if(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
DrawScene();
}
return msg.wParam;
}

Сначала мы получаем сообщение с помощью функции GetMessage(), затем отправляем это сообщение системе для обработки. Для отправки сообщений у нас есть две функции: TranslateMessage() и DispatchMessage(). В играх нужно чтобы всегда обновлялось изображение. Обновление происходит в свободное время, когда не приходит никаких сообщений. Для того, чтобы узнать, пришло ли сообщение, или нет, мы используем функцию PeekMessage(). Она возвращает значение true, если поступило сообщение, а затем мы его дальше обрабатываем. А если эта функция вернула значение false, то мы обновляем нашу картинку (то есть, вызываем функцию, в которой мы рисуем сцену).
Вот и все! Теперь откомпилируйте вашу программу и полюбуйтесь на беленькое окошко, которое отвечает всем требованиям профессионализма! :) Закончим создание каркаса в следующем уроке!
Alex Salo

Навигация
Назад | Список уроков
Используются технологии uCoz