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

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


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

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

Выпуск 21. (от 20 февраля 2004 года)
Вывод bmp изображений.

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

Обычно, перед тем как начать новый выпуск рассылки, я просматриваю предыдущие. Они помогают мне разбудить вдохновение, а заодно и почуствовать "контекст", атмосферу рассылки, существующую на настоящий момент, ну и конечно, оценить (весьма приближенно) уровень знаний читателей и понять их потребности.

И что же я вижу? То, что я уже повторял много раз. Большинству уже давно пора начинать свою первую игру (надеюсь, многие уже давно это сделали). Почему я так часто это повторяю? Да потому что я прекрасно помню то время, когда я сам думал, что сначала надо основательно выучиться, а уже потом и садиться за написание игр. Ничего подобного. (И, похоже, это заблуждение очень распространено.) Учиться нужно в процессе работы. Очень сложно учиться "чему-нибудь", не зная, зачем это понадобится. (Эх, а ведь именно так нас и учат в школах и ВУЗах...). Ну, например, что значит "изучить DirectX"? Так и хочется спросить - "а зачем вам?". Если вам крайне необходимо использовать аппаратное ускорение вашей видеокарты, если GDI просто тонет в море графических объектов - тогда да. Тогда другой вопрос - "что конкретно вы собираетесь отрисовывать на экране?". Мда, напоминает допрос с пристрастием, проводимый ехидным автором рассылки. На самом деле я просто хочу показать, что прежде чем тратить время на изучение чего либо, желательно задать себе пару вот таких вопросов. Прежде чем отправляться в путь, нужно знать, куда ты хочешь придти.

Еще один интересный момент, на котором, помнится, я уже заострял ваше внимание. Чем навороченный 3D-shooter с мощным графическим движком отличается от простенькой двумерной стрелялки? С точки зрения игрового процесса - абсолютно ничем. Все отличие состоит лишь в отображении на экране. И это еще один довод в пользу того, что незнание современных графических технологий не должно служить препятствием при создании игр. Вспомним слова одной статьи с сайта Школы: "Если вас интересует только лишь графика, то стоит ли вам вообще изучать создание игр? Займитесь графическими технологиями." И в этом случае хорошей школой для вас будет демосцена (demoscene). (Если вы не знаете, что это такое, наберите это слово в поисковике, сходите на парочку сайтов и скачайте несколько demo или 64k intro. Вам понравится.)

Последний довод в пользу того, почему уже пора садиться за игру. Вы можете спросить - "а кому захочется играть в мою простую игру?" Поверьте, желающих гораздо больше, чем вы можете представить. Ну, взять к примеру, Moorhuhn. Да чего уж там, возьмем любой пасьянс. Наверняка каждый хоть раз пытался разложить его. Так что, были бы игры - игроки найдутся. Даже несмотря на то, что "кажется, что большинство считает вот так". Как считает большинство, можно определить статистическими опросами, которые как раз-таки показывают, что большинство за пасьянсы. А вот кричать громче всех, создавая иллюзию большинства, будет как раз меньшинство плюс рекламные акции "покупайте нашу новую игру с революционным движком". Ну скажите, разве наличие "каунтер-страйка" в игровых клубах это показатель? Пожилой Семен Семеныч все равно в клуб не пойдет, а будет дома резаться с компьютером в преферанс или шахматы (все совпадения имен и фактов случайны).

Кстати меня всегда поражали "веяния моды". Так и хочется вспомнить одно излюбленное мной выражение "ща модно в кваку". Увы, устарело - в кваку ща уже давно не модно. Теперь у нас "кантра ришает!!!111". Когда-то я в ролевых играх просто души не чаял (как раз, когда было модно в кваку). Так нет же, были только Daggerfall и Might&Magic. А теперь вот стало модно, теперь только и слышно - "еще одна новая ролевая игра". И откуда только такая мода пошла?

Впрочем, это все лирические отступления. Итог такой - делайте такие игры, которые вам самим нравятся. Игры - это не только графика.

И все же, игра из кружков и квадратиков смотрится не очень привлекательно. Так почему бы нам наконец не изучить вывод на экран растровых изображений, попросту bmp файлов? Это совсем не сложно, не сложнее, чем рисовать прямоугольники. Точно так же, как мы передвигали по экрану прямоугольный объект, стирая его в старом месте (точнее, не стирая, а закрашивая фоном) и выводя в новом, мы можем передвигать и любую картинку, например изображение игрока. Можно поступить еще проще - по очереди выводить целиком весь фон, и уже потом игрока и другие объекты, согласно их расположению. Конечно мерцать будет очень заметно, и для снижения мерцания придется применить двойной буфер, а может быть и перейти (многие наверное скажут - наконец-то!) к DirectX (можно и к OpenGL, но использовать его для растра вместо DirectDraw - стоит ли?). Но все это будет не сегодня. Сегодня мы просто выведем на экран изображение из файла bmp.

Сразу приведу исходный код программы с комментариями. Когда будете его компилировать, не забудьте положить в рабочий каталог проекта какую-нибудь bmp картинку с тем же именем и с такими же размерами, что мы используем в программе (или измените их в тексте), иначе программа работать не будет. Если изменяете размеры, то желательно, чтобы картинка была меньше размеров нашего окна.




#include <windows.h>


// На этот раз я решил обойтись без #define и прописать все
// константы прямо в тексте программы. Так что, смотрите внимательно.


LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);

int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hprev, PSTR cmdline, int ishow)
{
  
     HWND hwnd;
     MSG msg;
     WNDCLASSEX wndclassex = {0}; 
 
 char class_name[] = "winclass";

 wndclassex.cbSize = sizeof(WNDCLASSEX);
     wndclassex.style="CS_HREDRAW" | CS_VREDRAW;
     wndclassex.lpfnWndProc = WinProc;
     wndclassex.hInstance = hinstance;
 wndclassex.hIcon = LoadIcon(hinstance,IDI_APPLICATION);
     wndclassex.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
     wndclassex.lpszClassName = class_name;

    RegisterClassEx(&wndclassex);


    hwnd = CreateWindowEx(NULL, 
    class_name, 
    "Loading bmp", 
    WS_OVERLAPPEDWINDOW, 
    CW_USEDEFAULT, 
    CW_USEDEFAULT,
    450,  // Ширина окна
    325,  // Высота окна
    NULL, 
    NULL, 
    hinstance, 
    NULL); 

 
 if(!hwnd)
 {
  UnregisterClass(class_name,hinstance);
  return EXIT_FAILURE;
 }

 
     ShowWindow(hwnd, ishow);
     UpdateWindow(hwnd);



/*
 Итак, сперва нужно загрузить изображение из файла.
 Сначала мы создаем объект HBITMAP, также, как мы создавали
 кисти и перья. Разумеется, таких объектов может быть
 несколько, так же, как и кистей или перьев.

 Затем мы загружаем из файла картинку функцией LoadImage, и
 полученное значение присваиваем переменной hbitmap (проведя
 приведение к типу HBITMAP - мы ведь можем загрузить
 не обязательно изображение bmp, а, например, курсор).

 При загрузке мы указываем имя файла, а также ширину и высоту
 изображения(нули означают то, что функция сама должна определить
 ширину и высоту по данным из файла).

*/
 HBITMAP hbitmap = (HBITMAP)LoadImage(hinstance,"MyBitmap.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
 
 // Получаем контекст устройства окна
 HDC hdc = GetDC(hwnd); 

 // Создаем "совместимый" контекст устройства, т.е. такой же, 
 // как и контекст устройства окна.
 // В нем мы будем хранить текущее изображение.
 HDC image_dc = CreateCompatibleDC(hdc); 
    
 // Выбираем в этот контекст картинку и сохраняем старую
 HBITMAP old_hbitmap = (HBITMAP)SelectObject(image_dc, hbitmap);

 
 // Теперь мы готовы вывести ее на экран
 /* Мы делаем это, выполняя "блиттинг" или копирование
    битов изображения из одного контекста в другой.
    Сейчас картинка находится в image_dc и начинается в 0,0.
    Мы вызываем функцию BitBlt, указывая:
    hdc - контекст куда мы копируем.
    50,50 - координаты верхнего левого угла, куда мы копируем
    320,192 - ширина и высота картинки в пикселях
    (Важный момент - мы можем копировать только часть картинки.
    Это может пригодиться, если мы не хотим плодить много
    маленьких объектов HBITMAP, а хотим поместить их все
    в одном файле, и потом "вырезать" их из нужного места.
    Обычно в играх это именно так и делается).
    image_dc - откуда мы копируем.
    0,0 - левый верхний угол откуда мы копируем
    (изменяйте это значение, если нужно скопировать часть
    bmp картинки с определенного места).
    SRCCOPY - растровая операция "копировать все без изменения".
 */

 BitBlt(hdc,50,50,320,192,image_dc,0,0,SRCCOPY);

/*************** вот и все!!! картинка на экране *************************/

 
 while(1)
 {
  if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
  {
   if(msg.message == WM_QUIT)
    break;
    
   TranslateMessage(&msg);
   DispatchMessage(&msg);
  }
  else
  {
   
  }

 } 

// Освобождаем память

 // Восстанавливаем текущий объект
 SelectObject(image_dc,old_hbitmap); 

 // Удаляем созданный нами объект
 DeleteObject(hbitmap); 

 // Удаляем созданный нами контекст устройства
 DeleteDC(image_dc); 

 ReleaseDC(hwnd, hdc); 

 UnregisterClass(class_name,hinstance);

  return msg.wParam;
}



LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{
    PAINTSTRUCT ps;
   
 switch(message)
    {
  
  case WM_PAINT:
   BeginPaint(hwnd, &ps);
   EndPaint(hwnd, &ps);
    return 0;

  case WM_CLOSE:
  case WM_DESTROY:
 
   PostQuitMessage(0);
    return 0;
    
 } 

    return(DefWindowProc(hwnd, message, wparam, lparam));
}



Еще несколько комментариев по тексту программы. Во-первых, мы не проверяем, загрузилась ли картинка. Желательно вводить такие проверки. А во-вторых, при перерисовке экрана картинка больше не появляется (так как мы выводим ее только один раз). Если сделать переменные "hdc" и "image_dc" глобальными и скопировать BitBlt() между BeginPaint() и EndPaint() в WM_PAINT, то это можно исправить.

Но стоит ли это делать - решать вам. Я показал лишь метод и ничего больше. А воспользоваться им нужно так, как того требует ваша игра. Например, можно загрузить все объекты при инициализации и вызывать последовательно BitBlt() для вывода изображений. Интересно, сможете ли вы переделать лабиринт из выпуска 18 так, чтобы вместо квадратиков и кружков выводились изображения? Это совсем несложно. Нужно просто заменить функции Ellipse и Rectangle на соответствующие BitBlt.

И еще кое-что. Вы наверное заметили, что мы выводим только прямоугольные изображения. А как быть с изображениями неправильной формы? Можно конечно, если фон игры однороден, просто делать фоновые области изображений таким же цветом, но чаще приходится применять маски прозрачности. И это будет еще одной темой на будущее.

Евгений Казеко.
kazeko@list.ru
www.gamecoder.nm.ru

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

Рассылка "Создание компьютерных игр", выпуск 21.
Выпускается еженедельно по пятницам.
Архив рассылки вы найдете по адресам http://subscribe.ru/catalog/comp.games.gamecoder и http://www.gamecoder.nm.ru/subscribe.htm.



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

В избранное