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

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


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

Рассылка "Создание компьютерных игр".

Выпуск 8.

Здравствуйте, друзья!

Те из вас, кто читал предыдущий выпуск, наверняка помнят советы от Евгения WidowMaker для новичков. Сегодня Евгений поделится с вами вводным рассказом о COM и DirectX. Однако для тех, кто "совсем начинает", тема может показаться сложноватой, поэтому сперва выскажусь сам.

Если вы один из тех, кому кажется, что вы ничего не понимаете в программировании и никогда ничего не поймете, хотя очень стараетесь, то мои слова будут как раз для вас. Вы либо ленивы, либо пытаетесь перепрыгнуть через десять ступенек сразу. С ленью боритесь с помощью вашего страстного желания стать создателем игр, что касается желания "мгновенного прогресса" - поверьте, это иллюзия. Рост и развитие - процесс постепенный, и горе тому, кто пытается обойти этот неизменный закон природы. Такого человека ждет разочарование и ничего более.

Учитесь постепенно, не торопитесь. Сделайте ваше обучение последовательным. Я рекомендую последовательность "С++ -> Windows API -> DirectX/OpenGL", но она может быть любой другой по вашему вкусу. Можете не изучать API, но язык С или С++ - это тот фундамент, на котором держится все остальное.

Теперь для тех, кого парализует страх перед неизвестным. Найдите компилятор, купите или скачайте бесплатный. Кстати очень рекомендую Dev C++ с http://www.bloodshed.net. Узнайте, как в нем вводить текст программы, как компилировать ее и запускать. Скачайте проект первой обучалки по языку С с сайта Школы для Visual C++ или просто скопируйте и вставьте текст программы в окно редактирования вашего компилятора. Скомпилируйте и запустите программу.

Все, я вас поздравляю, начало есть! Проделайте то же самое для остальных обучалок. После этого вы сможете понять статью Евгения по COM/DirectX.

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

Сразу предупреждаю людей открывавших справку SDK, что здесь они ничего нового не узнают. Письмо получилось несколько "корявым", потому не исключены ошибки и упущения некоторых важных моментов. Я не противник критики...

В качестве примеров я использовал стандартные примеры из справки (дабы случайные очепятки не испортили жизнь "новичкам" ); у меня сейчас SDK8 (ну не нашел я 9, а качать "жаба давит":)), потому в конце интерфейсов идет цифра 8. Эта "статья" имеет свой целью не столько научить, а сколько пробудить желание и интерес к чтению справки. Поверьте это просто, а главное ИНТЕРЕСНО (даже интересней, чем делать игры)! Посему надеюсь мой грубый стиль изложения не отобъет у вас охоту учиться, а даже наоборот...

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

СОМ...
Помните зачем нужны были dll? Если нет, то DLL-Dinamic Load Library (Динамически Загружаемая Библиотека). Т.е., к примеру, ваша программа 1 использует набор некоторых функций F1,F2,F3...Fn. И есть другое приложение 2, которое тоже их использует. Запуская оба приложения, мы создаем в памяти ПК по две копии F1,F2,F3...Fn. Это, надеюсь, всем понятно. Эту накладку в ресурсах решают через dll.Пусть в файле, к примеру, myfunc.dll содержатся все перечисленные функции, тогда при необходимости их использовать приложение 1 или 2 загрузит эту dll в память. Теперь и 1 и 2 приложения могут обращаться к функциям библиотеки, не зависимо от того, какое приложение ее загрузило. Вот. Этот механизм описан в справке практически любого компилятора.

Итак, DLL-это набор функции доступных для загрузки в память. СOM - та же DLL, но в контексте ООП. Это вроде примочки имитирующей классы. В роли классов в COM выступают интерфейсы (ну не знаю почему такое страшное название:).

А сама библиотека, как семейство классов. Важно понять разницу между СОМ-объектом и интерфейсом! Объект поддерживает множество своих интерфейсов. Всегда работать с объектом мы будем используя какой-то интерфейс, без него никак. Интерфейсы образуют иерархию, как классы. Чтобы это было понятней рассмотрим иерархию версий интерфейсов. Каждая новая версия DirectX приносит нововведения(новые функции) или улучшения старых функций. Чтобы старые приложения продолжали работать в ОС с новой версией DirectX они в коде явно указыват интерфейс(к примеру, IDirect3D7), а новая библиотека DirectX9 поддерживает интерфейсы: IDirect3D2, IDirect3D3, IDirect3D4, IDirect3D5, IDirect3D6, IDirect3D7, IDirect3D8, IDirect3D9. Но каждый новый интерфейс включает в себя все функции предыдущего. И хотя интерфейс IDirect3D9 включает в себя все функции IDirect3D7 нам все равно в старом приложении можно будет работать с IDirect3D7. Однако COM-классы, это только набор функций для обработки данных. Для хранения данных они не предназначены (в основном). Перед созданием любого COM объекта, СОМ необходимо инициализировать функцией CoInitialize (после окончания работы с СОМ вызывают CoUninitialize).

Для создания (иногда это равнозначно загрузке) COM объекта используется функция CoCreateInstance, она принимает 5 параметров:

  • идентификатор класса (тот кто работал с ресурсами может проводить аналогии), обычно с приставкой CLSID_ .
  • указатель связи (я смутно представляю себе, что это), почти всегда NULL.
  • параметр указывающий о типе памяти СОM (где-то так), почти всегда CLSCTX_INPROC_SERVER.
  • идентификатор интерфейса, с которым мы бы хотели работать, обычно с приставкой IID_ .
  • адрес указателя (указатель на указатель) который будет указывать на объект (его приводят к void*, для универсальности функции CoCreateInstance, чтобы ее не нужно было переписывать под каждый отдельный тип)

Пример:

IDirect3D8* game_D3D = NULL; //можно написать и LPDIRECT3D8 вместо IDirect3D8*
.......................................
CoInitialize( NULL ); //почти всегда NULL
........................................
CoCreateInstance( CLSID_Direct3D8, NULL, CLSCTX_INPROC_SERVER, IID_IDirect3D8, (LPVOID*) &game_D3D );
/* LPVOID - указатель на тип void (все типы с начальной буквой LP и P являются указателями на эти типы)*/
..........................................
CoUninitialize();
Такой пример объявляет указатель на интерфейс(первый символ 'I') IDirect3D8, инициализирует СОМ, создает объект Direct3D8 и присваивает указателю game_D3D адрес созданного объекта с интерфейсом IDirect3D8.

А если нам нужен другой интерфейс? Создавать еще один объект? Вовсе нет, можно "перемещаться" по интерфейсам COM объекта с помощью функции QueryInterface, принадлежащей интерфейсу IUnknown (базовому интерфейсу всех СОМ-объектов). Этот интерфейс содержит всего 3 функции, но они обеспечивают функционирование любого СОМ-объекта. Это:
  • AddRef
  • QueryInterface
  • Release
Эти три функции являются теми тремя "столбами" на которых и стоит COM. Поэтому рассмотрим их подробнее.

Метод AddRef увеличивает счетчик ссылок СОМ-объекта на 1. Этот счетчик показывает сколько интерфейсов в данный момент получено с этого объекта. При создании объекта мы указываем интерфейс который хочем получить и автоматически устанвливаем значение счетчика в 1. Вызывать этот метод вручную, не рекомендую (да это и не нужно, если только вы не собираетесь разрабатывать новые COM-объекты и технологии)!

Метод QueryInterface дает возможность получить доступ к другому интерфейсу объекта. Но для этого надо быть уверенным в том, что это другой интерфейс поддерживается объектом (иначе быть беде:( ). Этот метод дает возможность получать доступ к новым возможностям (интерфейсам) СОМ-объектов. Прототип: HRESULT QueryInterface( REFIID riid, LPVOID* ppvObj ); riid - пункт четвертый в CoCreateInstance. ppvObj - адрес указателя для получаемого интерфейса Вызывая этот метод мы не явно вызываем и AddRef.

Метод Release уменьшает счетчик ссылок СОМ-объекта на 1. Если счетчик равен 0, то СОМ-объект удаляется. Теперь понятно для чего нужен это счетчик. Если мы удаляем интерфейс 1 объекта А, но у нас есть интерфейс 2 (полученный от этого же объекта А), то было бы не правильно удалить тот "ресурс"(объект А) из которого "берет" функции интерфейс 2. Это приведет к... в общем приведет к access violation at...read of adress 0xffffffff и прочей гадости :)

Так для получения из предыдущего примера интерфейса IDirect3DDevice8 нужно написать следующее

.................................................
IDirect3DDevice8* game_D3DD = NULL; //можно написать и LPDIRECT3DDEVICE8 вместо IDirect3DDevice8*
game_D3D->QueryInterface( IID_IDirect3DDevice8, (LPVOID *)&game_D3DD)));
..................................................

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

Примечание: большинство методов расмотренных ниже возвращают тип HRESULT - универсальный результат, это 32 разрядное число. При работе с ним его могут рассматривать как две части: старшую и младшую, или int. Для него определено множество константных значений в Winerror.h. Существует множество значений ошибок и успехов, потому для общей проверки на ошибку или успех используют макросы FAILED и SUCCEEDED, который возвращают TRUE, если константа трактуется как одна из ошибок, и TRUE, если константа трактуется как одна из успехов, соответственно.

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

Microsoft сильно постаралась завернуть работы с "сырыми" СOM объектами в более наглядные косвенные методы, специфичные для каждого отдельно взятого интерфейса (к примеру, Direct3DCreate8 - метод создания объекта Direct3D c интерфейсом IDirect3D8). Если мы создаем объекты(получаем доступ к новым интерфесам) косвенными функциями, то не нужно вызывать CoInitialize и CoUninitialize, они уже вызваны или будут вызваны автоматически (удобно не правда ли). Практически всегда вы будете использовать косвенные методы работы с COM (но знакомство с "классикой" было не лишним). А теперь, когда Вы, читатели, с СОМ друзья "не разлей вода", то можно привести и более конкретные примеры.

Итак, чтобы использовать в приложении возможности DirectX мы должны, прежде всего, создать объект Direct3D (в 8 версии и выше DirectDraw включен в Direct3D, может и раньше, точно не знаю) c интерфейсом IDirect3D. Вот так:

LPDIRECT3D8 g_pD3D = NULL; //указатель на интерфейс IDirect3D8
if ( NULL == ( g_pD3D = Direct3DCreate8( D3D_SDK_VERSION ) ) ) return E_FAIL;
/*Если не повезло, то возвращаем E_FAIL (это define значение типа HRESULT)*/

D3D_SDK_VERSION - define константа о версии DirectX (так проверяют правильность версий h-файлов). Пример довольно прост, он состоит из вызова всего одной функции и проверки выделения памяти на NULL.После использования объекта мы вызываем Release:

.................................... //основная программа
g_pD3D->Release(); //делают это обычно перед "разрушением" окна.

Далее нам нужно получить интерфейс для рисования и т.п. Он называется IDirect3DDevice8 и получается косвенным методом CreatDevice интерфейса IDirect3D8. Для его получения необходимо задать и передать в CreatDevice структуру D3DPRESENT_PARAMETRS.
/*методом GetAdapterDisplayMode интефейса IDirect3D8 получаем информацию (структуру D3DDISPLAYMODE) о текущем режиме экрана которую->>*/
D3DDISPLAYMODE d3ddm;
if ( FAILED( g_pD3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm ) ) ) return E_FAIL;

D3DPRESENT_PARAMETERS d3dpp; //структура для хранения настроек IDirect3DDevice8(в ней гораздо больше элементов)
ZeroMemory( &d3dpp, sizeof(d3dpp) ); //(заполняем блок памяти нулями)
d3dpp.Windowed = TRUE; //(мы будем работать в оконном режиме)
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; //устанавливаем режим Swap (для Windowed)
d3dpp.BackBufferFormat = d3ddm.Format; //которую ->>используем здесь для определения формата заднего буфера (если Windowed=true)
Теперь мы готовы создавать(получать) интерфейс IDirect3DDevice8:
IDirect3DDevice8* g_pd3dDevice = NULL;

if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_pd3dDevice ) ) )
теперь в некоторой функции Rendering() мы пишем:
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 ); //очистка заднего буфера
/*подробности о каждом параметре функции см. в справке SDK*/
Все рисование всегда заключено между вызовами BeginScene() и EndScene() :
g_pd3dDevice->BeginScene(); //начало
.........................................
// Рисование на заднем буфере
........................................
g_pd3dDevice->EndScene(); //конец
Теперь меняем указатели на буферы:
g_pd3dDevice->Present( NULL, NULL, NULL, NULL ); //смена буферов (Flip)

/*подробности о каждом параметре функции см. в справке SDK*/
В обработчик события WM_DESTROY вставляем код удаления всех наших интерфейсов, а соответственно и объекта СОМ:
if( g_pd3dDevice != NULL) g_pd3dDevice->Release();
if( g_pD3D != NULL) g_pD3D->Release();

Это еще не полностью функционирующий код, но вы сами можете сделать его рабочим (для этого не требуются знаний DirectX). В следующий раз я расскажу как рисовать на заднем буфере и буфере векторов. Ну а если "чешутся" руки, то можете таки открыть второй урок в справке SDK и сами что нибудь сваять...

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

Мало? Да...На все нужно время...

С Уважением, Евгений aka WidowMaker mailto:widowmakerreborn@mail.ru

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

Рассказ Евгения достаточно сжатый, несмотря на объем. Впрочем, как он сам говорит, при написании "ставилась цель не столько научить, а сколько пробудить желание и интерес к чтению справки". И я считаю, что это правильно. Читайте, разбирайтесь, пробуйте. Приходите в наш форум http://www.forums.gameschool.ru, чтобы задать вопросы. Кстати в форуме я бываю гораздо чаще, чем смотрю почту (это если вы хотите задать мне вопрос и получить ответ быстрее чем через 2 недели).

Напоследок не удержусь от небольшого "урока для новичков". Вы не стали читать статью, потому что споткнулись на незнании слова "SDK"? Да вы, уважаемый сударь\сударыня не новичок, вы просто ламер. Только не обижайтесь. Во-первых самообман никому еще не помогал, во-вторых этот прискорбный факт легко исправляется и означает всего-лишь то, что вам нужно многому учиться. Например, учиться пользоваться системами поиска в Интернете. Набираете в строке... ну например "DirectX SDK" и ищете. Но все же я подскажу. SDK это software development kit, и вам нужно будет скачать DirectX SDK или поискать в продаже на диске. С книгами Ламота он идет в комплекте (книги можно купить по ссылке с gameschool.ru).

До встречи!

Евгений Казеко.
evgeniy@kazeko.com


Subscribe.Ru
Поддержка подписчиков
Другие рассылки этой тематики
Другие рассылки этого автора
Подписан адрес:
Код этой рассылки: comp.games.gamecoder
Отписаться
Вспомнить пароль

В избранное