DirectInput: Все, что нужно знать...
Автор: Антон В. Звягинцев
Привет!
Любая программа без пользовательского ввода превращается в статическую картинку, а то и в слайд шоу или кино. Но ведь мы смотрим не телевизор - даешь интерактивность! Именно эту проблему мы и будем сегодня обсуждать. Для нашего обсуждения я не стал сочинять чего-нибудь необычного - я воспользовался примерами из DirectX 7 SDK. Они достаточно хорошо иллюстрируют то, с чем мы сегодня столкнемся. Для начала обговорим условия компиляции и линковки приложений - кроме используемой нами DINPUT.LIB, при линковке тебе нужно будет включить в проект DXGUID.LIB для приложений, у которых не определена INITGUID (Использование INITGUID - старый стиль включения GUID номеров в приложение, использующее COM модель).
Ну, а для начала немного теории. Для чего нужны функции DirectInput, ты уже знаешь. Чем же DirectInput API лучше, чем стандартные возможности, предоставляемые приложению ядром? Ну, конечно своей скоростью и своими возможностями:
Получаемый формат данных:
Схемы получения данных:
Теперь тебе не обязательно зависеть от "оконной" процедуры и сообщений Windows - ты можешь в любом месте программы и любой промежуток времени прочитать данные с устройства.
Рассмотрим схему инициализации для DirectInput. Поскольку DirectInput как и практически все компоненты DirectX построен на основе модели COM от Microsoft, общая структура работы с API ничем не будет отличаться от работы с другими компонентами DirectX API. Первое, что должно сделать приложение, использующее DirectInput API, это получить доступ к объекту DirectInput через соответствующий интерфейс. Сделать это можно вызовом DirectInputCreate() или DirectinputCreateEx(). Во всех примерах MS кроме одного используется первый вариант. Вариант два появился в версии DirectX 7.0. Рассмотрим параметры этих функций. Для этого ты можешь воспользоваться документацией.
DirectInputCreate(HINSTANCE hInst, DWORD dwVer, LPDIRECTINPUT *lplpDI, LPUNKNOWN pU);
hInst - идентификатор экземпляра самого приложения получаемый в WinMain().
dwVer - версия объекта DirectInput. Обычно подставляется макроопределение DIRECTINPUT_VERSION заранее определенное в DINPUT.H для установленной версии DirectX SDK.
lplpDI - указатель на указатель интерфейса, который примет в себя информацию.
pU - указатель наследования или агрегирования COM. Нас он не интересует - достаточно передать NULL.
DirectInputCreateEx(HINSTANCE hInst, DWORD dwVer,REFIID ri, LPVOID *ppO, LPUNKNOWN pU)
hInst - то же, что и для варианта DirectInputCreate()
dwVer - то же, что и для варианта DirectInputCreate()
ri - идентификатор требуемого интерфейса. Может принимать значения IID_IDirectInput,IID_IDirectInput2,IID_IDirectInput7.
ppO - указатель на указатель интерфейса, который примет в себя информацию. Аналогичен с последним параметром предыдущей функции.
pU - указатель наследования или агрегирования COM. Аналогичен с последним параметром предыдущей функции.
Как ты видишь — все достаточно просто. После получения указателя на интерфейс, нужно "создать" устройство от которого ты хочешь получать данные. Под устройством подразумевается все, что поддерживает DirectInput, например клавиатура, мышь, джойстики и т.п. Для создания устройств также существуют две функции - CreateDevice() и ее -Ex вариант (появился в 7 версии DirectX):
CreateDevice(REFGUID rg,LPDIRECTINPUTDEVICE *lplpDI,LPUNKNOWN pU);
rg - уникальный идентификатор устройства. Если ты еще не привык, что вся работа идет с некими Global Unique IDentifer'ами, советую поскорее привыкнуть ;). Для стандартных устройств заранее определены значения: GUID_SysKeyboard для клавиатуры; GUID_SysMouse для мыши. Остальные идентификаторы устройств (например джойстики) можно получить вызовом EnumDevices().
lplpDI - указатель на указатель интерфейса, с помощью которого мы будем впоследствии работать с устройством.
pU - все то же агрегирование.
CreateDeviceEx(REFGUID rg,REFIID ri,LPVIOD *ppO,LPUNKNOWN pU);
rg - то же, что и в первом случае.
ri - идентификатор требуемого интерфейса. Может принимать значения IID_IDirectInput,IID_IDirectInput2,IID_IDirectInput7.
ppO - указатель на указатель интерфейса, который примет в себя информацию. Аналогичен параметру lplpDI предыдущей функции.
pU - указатель наследования или агрегирования COM. Аналогичен с последним параметром предыдущей функции.
После того, как мы получили указатель на интерфейс устройства, воспользуемся им для установки формата данных для этого устройства. Функция SetDataFormat() получает всего один параметр - указатель на структуру описывающую формат данных для устройства. Для стандартных устройств заранее определены глобальные переменные: c_dfDIKeyboard, c_dfDIMouse, c_dfDIMouse2, c_dfDIJoystick, c_dfDIJoystick2. Итак - формат данных установлен - DirectInput знает тип устройства. Что же теперь? А теперь мы должны установить уровень доступа. Довольно важно определиться заранее (как и с форматом принимаемых данных) - какой доступ необходим.
Уровни кооперации:
Что еще полезно знать:
char cDummy = cBuff[DIK_ESCAPE];
занесет в сDummy байт данных по клавише Esc. Для определения нажата ли клавиша, достаточно проверить последний бит этого байта:
if (cBuff[DIK_ESCAPE] & 0x80) { // Клавиша нажата! }
Для того, что бы установить буферизированный ввод с клавиатуры, после установки уровня кооперации нужно установить размер буфера:
DIPROPDWORD dipdw; dipdw.diph.dwSize = sizeof(DIPROPDWORD); dipdw.diph.dwHeaderSize = sizeof( DIPROPHEADER); dipdw.diph.dwObj = 0; dipdw.diph.dwHow = DIPH_DEVICE; dipdw.dwData = BUFFER_SIZE; hr = g_pKeyboard->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph );
где BUFFER_SIZE - размер буфера.
По умолчанию мышь возвращает относительные координаты, а джойстик абсолютные.
По завершении приложения ты должен "убрать за собой" - "уступить" все устройства, и удалить объекты DirectInput вызовом Release().
Это был лишь небольшой обзор, и в нем много чего было опущено (например, те же устройства с обратной связью (Force Feedback)). Не смотря на это, он должен был дать тебе достаточно информации для дальнейшего глубокого изучения.
19 февраля 2002
Комментарии [9]