Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Создание компьютерных игр


Информационный Канал Subscribe.Ru

Cоздание компьютерных игр.
Выпускается еженедельно по пятницам.
Автор рассылки - Евгений Казеко.

Выпуск 15. (от 14 ноября 2003 года)
Ничего сложного.

--------------------------------------------------------------

Этот выпуск я хочу начать со слов благодарности всем поклонникам рассылки, тем, кому небезразлична ее судьба, кто с нетерпением ждет следующих ее выпусков, присылает ценные советы... Спасибо вам за вашу поддержку! И спасибо, что дождались очередного выпуска, который наконец-то выходит после столь долгого перерыва. Многие боялись, что рассылка закроется. Могу вас заверить - этого не случится никогда. Конечно, в жизни бывает всякое. Рассылка может приостановить свою работу, как это, увы, случалось уже дважды. Рассылку может закрыть администрация, и в этом случае придется открывать ее заново. Но выпуски будут выходить, и я буду прилагать все усилия, чтобы каждую неделю вы узнавали для себя что-то новое о создании компьютерных игр.

А теперь поговорим о теме сегодняшнего выпуска. Похоже, что стремясь сделать выпуски как можно более информативными, я отошел от доброй традиции рассказывать обо всем очень подробно и предельно ясно. В результате многие новички совершенно запутались в освоении Win32 API, о чем я узнал из их писем.

На самом же деле, написать приложение Windows не так уж сложно. Конечно, код программы для Windows на первый взгляд кажется уж очень непонятным, особенно после консольных приложений. И знаю по себе, что победить возникающий при знакомстве с исходниками Windows-программ страх очень непросто, особенно, когда некому помочь. Ну что же, будем разбираться вместе. Конечно, для этого придется немного вернуться назад, да и написание тетриса придется отложить на время. Но это будет нам только на пользу. Тем же, кто прекрасно ориентировался в текстах программ предыдущих выпусков, хочу сказать, что, хотя к нашему тетрису мы вернемся еще нескоро, уже через два выпуска я буду раскрывать новые темы, важные для создания игр (и тетриса в том числе). То есть, подождать придется всего пару недель. Кроме того, я постараюсь сделать так, чтобы и в этих выпусках вы нашли для себя что-нибудь новое и занимательное.

Итак, остановим наш бег и оглядимся. Мы знаем основы языка С. Мы знаем его синтаксис. Этого вполне достаточно для плавного перехода к созданию приложений Windows. (Для тех, кто только что присоединился к нам - чтобы понять, чем мы здесь занимаемся, ознакомьтесь, пожалуйста, с архивом рассылки. Только будьте любезны, не присылайте мне больше писем с вопросом, где его взять - ссылки вы найдете в конце каждого выпуска.)

Вспомним нашу первую программу для Windows, которая выводила на экран пустое окно. Как бы мы создавали окно, не имея для этого стандартных средств Windows? (например, в эпоху DOS). Мы бы сперва описали его свойства, затем нарисовали бы это окно на экране, исходя из указанных свойств (не исключено, что пришлось бы работать напрямую с видеопамятью), после чего стали бы ждать действий пользователя. Разумеется нам нужно было бы определить действия программы в каждом случае. Например, если пользователь захотел изменить размер окна мышью, нам нужно стереть из видеопамяти старое окно и нарисовать окно нового размера.

Нам пришлось бы все это делать в былые времена, но сейчас все уже сделано разработчиками Windows. Нам достаточно лишь вызвать стандартные функции Win32 API, которые загружаются в память при запуске компьютера.

Да, чтобы создать полнофункциональное окно, способное откликаться на действия пользователя, требуется немного потрудиться. Но чтобы написать приложение Windows, нам совсем необязательно создавать окно. Сейчас мы сами убедимся в этом. Взгляните на текст программы:


#include <windows.h>

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    LPSTR szCmdLine, int iCmdShow)
{
     MessageBox (NULL, "Hello, Windows!", "Hello", 0) ;

     return 0 ;
}

Вот и все! Мы создали приложение Windows, не имеющее окна. Не правда ли, просто?

А теперь рассмотрим, что же здесь такого, что может испугать новичков. Конечно же, функция WinMain, которая заменила столь привычную для консольных приложений main(). На самом деле, ничего страшного в ней нет. Обычная функция - в скобках список параметров, а перед именем переменных тип возвращаемого значения (в Windows определены специфические типы, которые, как правило, легко узнать по записи большими буквами). И еще декларатор WINAPI, который, как говорит Андре Ламот, позволяет Windows правильно передать параметры в функцию. Эти параметры, как уже сказано, передает в программу сама система Windows при запуске данной программы. Они нужны прежде всего самой Windows, но могут пригодиться и нам. Первый параметр является дескриптором (ссылкой) на экземпляр данной программы в системе. Второй был нужен когда-то, чтобы указывать на программу, вызвавшую эту (сейчас этот параметр используется только для "обратной совместимости"). Третий хранит дополнительные значения при запуске из командной строки, а четвертый указывает, как запускается окно программы.

Я это рассказываю только для того, чтобы устранить страх перед "непонятным". В этой программе нам не понадобился ни один из этих параметров. Но функция WinMain всегда должна выглядеть только так.

Что же делает наша программа? Она вызывает стандартную функцию MessageBox для отображения сообщения Hello, Windows! Первый параметр этой функции указывает на родительское окно (у нас его нет, точнее, им является рабочий стол). Второй содержит выводимое сообщение. Третий - заголовок окошка с сообщением. А четвертый нужен для определения внешнего вида этого окошка, наличия или отсутствия кнопочек Yes, No, или каких-нибудь других.

По сути, мы воспользовались только двумя из этих параметров. Я надеюсь, при разборе этой программки не возникнет ничего сложного или непонятного. А чтобы совсем побороть страх перед приложением Windows, давайте возьмем и уберем WinMain. Правда, для этого придется создать консольное приложение вместо приложения Win32. Но это будет точно такое же приложение Windows. Убедитесь в этом сами. Итак, меняем WinMain на нашу старую добрую main() и компилируем консольное приложение:



#include <windows.h>

int main()
{
     MessageBox (NULL, "Hello, Windows!", "HelloMsg", 0) ;

     return 0 ;
}

И абсолютно ничего сложного. Теоретически, из консольных приложений можно вызывать любые стандартные функции Windows, главное делать это правильно. Но вот окно создать мы можем только в приложении Win32, так как нам потребуются для этого параметры из WinMain. И сейчас, мы пожалуй его и создадим, поняв, что ничего сложного в этом нет.


// Начнем как всегда с объявлений функций и заголовочных файлов.

#include <windows.h>

// Об этой функции разговор будет позже.
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;


// А вот и WinMain.
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{

/*   Так же, как и в main, задаем переменные. Как видите, все они
     являются структурами, которые определены в заголовочном файле.
     (Для тех, кто не понял - большие буквы это тип переменной, а 
     маленькие это ее имя).

     Первая переменная будет хранить ссылку на окно. Когда нам понадобится
     обратиться к окну, мы воспользуемся этой переменной.
     Вторая будет хранить сообщения, получаемые от пользователя.
     В третьей мы опишем свойства нашего окна.
*/

     HWND         hwnd ;
     MSG          msg ;
     WNDCLASS     wndclass ;

// Итак, отсюда мы и начинаем фактическое создание окна.


//   1) Опишем свойства окна. Создадим так называемый "оконный класс", на основе
//   которого мы сможем создавать однотипные окна, а точнее, просто заполним
//   поля структуры wndclass нужными значениями.


     wndclass.style="CS_HREDRAW" | CS_VREDRAW ;  // окно с перерисовкой
     wndclass.lpfnWndProc   = WndProc ;  // функция, которая будет обрабатывать сообщения
      // для этого окна
     wndclass.cbClsExtra    = 0 ;        // дополнительные свойства (нам не надо)
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ; // экземпляр программы (параметр из WinMain)
     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;  // пиктограмма окна
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;      // курсор окна
     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; // фон окна - белый
     wndclass.lpszMenuName  = NULL ;      // окно будет без меню
     wndclass.lpszClassName = "Window Class 1";  // И наконец, имя класса окна, которое
                                                 // мы будем использовать при создании окна


//   2) Структуру-то мы заполнили, но система еще не знает об этом оконном классе.
// Его нужно зарегистрировать.

     RegisterClass (&wndclass);


//   3) А вот теперь мы можем создать окно, и обращаться к нему, используя
//      переменную hwnd. 

     hwnd = CreateWindow ("Window Class 1",  // имя класса окна, на основе которого оно 
          // создается  
                          "Window",      // заголовок окна       
                          WS_OVERLAPPEDWINDOW, // тип окна - окно с переменным размером     
                          CW_USEDEFAULT,       // остальные параметры задаем по умолчанию   
                          CW_USEDEFAULT,              
                          CW_USEDEFAULT,              
                          CW_USEDEFAULT,              
                          NULL,                       
                          NULL,                       
                          hInstance,                  
                          NULL) ;                     

//   4) Выводим окно в системе (появится в диспетчере задач).     

     ShowWindow (hwnd, iCmdShow) ;

//   5) Фактически рисуем окно на экране

     UpdateWindow (hwnd) ;


// Вот и все! Теперь, когда окно нарисовано на экране, мы можем обрабатывать
// пользовательский ввод, принимая так называемые сообщения окна.
     
     while (GetMessage (&msg, NULL, 0, 0))
     {
          TranslateMessage (&msg) ;
          DispatchMessage (&msg) ;
     }

// Когда система посылает сообщение выхода, мы завершаем программу
     return msg.wParam ;
}



// А теперь об оконной процедуре. Именно она и обрабатывает пользовательский ввод и
// другие сообщения. Сообщений существует очень много, но сегодня мы будем обрабатывать
// лишь несколько из них, да и то формально.
// Ссылку на эту оконную процедуру мы указали при создании оконного класса.
// Первый параметр это переменная-ссылка на наше окно.
// Второй - сообщение, передаваемое системой (см.  while (GetMessage....)
// В третьем и четвертом система сохраняет дополнительные характеристики
// сообщения, например код нажатой клавиши. Мы еще столкнемся с этим не раз.

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
     
     switch (message)
     {
     case WM_CREATE:   // сообщение при создании окна
          
          return 0 ;

     case WM_PAINT:    // сообщение при перерисовки окна

          return 0 ;
          
     case WM_DESTROY:  // сообщение при уничтожении окна
          PostQuitMessage (0) ;
          return 0 ;
     }

     // А так мы обрабатываем все остальные сообщения, не указаные нами
     // в switch. Происходит обработка по умолчанию.
     return DefWindowProc (hwnd, message, wParam, lParam) ;
}


В заключение пара слов о столь пугающих именах переменных и типов. Вот например, третий параметр WinMain - szCmdLine. Что за тарабарщина? Это всего-навсего "венгерская нотация", согласно которой, для того, чтобы было сразу понятно, что за тип имеет переменная, перед ее именем пишется пара букв. Например - sz означает zero-terminated string или попросту строка. i используется для типа int, есть и другие обозначения. Наберите в поисковике "венгерская нотация" или "hungarian notation". Конечно, вы можете называть переменные так, как вам нравится, необязательно использовать венгерскую нотацию. А что касается специальных типов Windows, таких, как HINSTANCE, HWND, MSG, WNDCLASS, то к ним нужно просто привыкнуть. Таких типов в Windows очень много. Как правило, большинство из них - это структуры. (Вспомните, как мы с вами создавали структуру PLAYER).

Ну что же, очень надеюсь, что вот теперь точно все "тайны" раскрыты. А напоследок я еще раз посоветую - не ограничивайте обучение материалами рассылки. Читайте книги, ищите в Интернете. Информации по данному вопросу полно.

В следующий раз мы вновь поучимся рисовать в нашем окне, а также обработаем несколько нажатий клавиш.

До встречи через неделю!

--------------------------------------------------------------

Архив рассылки вы найдете по адресам http://subscribe.ru/catalog/comp.games.gamecoder и http://www.gamecoder.nm.ru/subscribe.htm.

Евгений Казеко.
kazeko@list.ru
www.gamecoder.nm.ru
-----------------------------
Рассылка "Создание компьютерных игр", выпуск 15.
Выпускается еженедельно по пятницам.



http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу

В избранное