netlib.narod.ru< Назад | Оглавление | Далее >

DirectInput и джойстик

Ну вот, и самое сложное для использования устройство. Самая трудная часть — это инициализация. Вы должны выполнить перечисление, чтобы обнаружить подключенные к системе джойстики. В процессе перечисления вы должны решить, какой джойстик будет использоваться и создать COM-объект для него. В этой книге мы рассмотрим только вариант с единственным джойстиком в системе.

// Убедитесь, что ранее инициализированы
// глобальный объект DirectInput g_pDI 
// и дескриптор родительского окна g_hWnd
IDirectInputDevice8 *g_pDIDJoystick = NULL;

BOOL CALLBACK EnumJoysticks(LPCDIDEVICEINSTANCE pdInst,
                            LPVOID pvRef)
{
    HRESULT hr;
    g_pDIDJoystick = NULL;

Начинается перечисление очень просто. Вы создаете глобальный объект IDirectInputDevice8 для использования джойстика. В начале перечисления вы присваиваете указателю на интерфейс значение NULL, указывающее, что ничего не найдено. После перечисления вы проверяете, осталось ли значение указателя равным NULL, что свидетельствует о том, что джойстики не были инициализированы.

Что касается аргументов функции перечисления, то pdInst — это указатель на структуру DIDEVICEINSTANCE, содержащую сведения о перечисляемом в данный момент устройстве. Вы можете получить необходимый для создания интерфейса устройства GUID из поля guidInstance этой структуры.

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

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

// Создаем объект устройства, используя глобальный объект DirectInput
if(FAILED(g_pDI->CreateDevice(pdInst->guidInstance,
                              &g_pDIDJoystick, NULL)))
    return DIENUM_CONTINUE;

// Устанавливаем формат данных
if(FAILED(g_pDIDJoystick->SetDataFormat(&c_dfDIJoystick))) {
    g_pDIDJoystick->Release();
    g_pDIDJoystick = NULL;
    return DIENUM_CONTINUE;
}

// Устанавливаем уровень кооперации
if(FAILED(g_pDIDJoystick->SetCooperativeLevel(hWnd,
                 DISCL_FOREGROUND | DISCL_NONEXCLUSIVE))) {
    g_pDIDJoystick->Release();
    g_pDIDJoystick = NULL;
    return DIENUM_CONTINUE;
}

Теперь необходимо установить свойства устройства. В это входит задание диапазона значений для осей джойстика, а также установка мертвой зоны. Требуемые значения заносятся в структуру DIPROPRANGE, которая применяется для установки диапазона в вызове IDirectInputDevice8::SetProperty.

Определение структуры DIPROPRANGE выглядит так:

typedef struct DIPROPRANGE {
    DIPROPHEADER diph; // Обсуждавшийся ранее DIPROPHEADER
    LONG         lMin; // Минимальное значение диапазона (по оси X или Y)
    LONG         lMax; // Максимальное значение диапазона (по оси X или Y)
} DIPROPRANGE, *LPDIPROPRANGE;

 

ПРИМЕЧАНИЕ
Структура DIPROPRANGE полезна, когда необходимо установить свойство, являющееся диапазоном допустимых значений (например, от 10 до 200 или от –1000 до 20).

Чтобы использовать структуру DIPROPRANGE нужно сначала объявить ее экземпляр и инициализировать его:

DIPROPRANGE dipr;

// Сначала очищаем структуру
ZeroMemory(&dipr, sizeof(DIPROPRANGE));

dipr.diph.dwSize       = sizeof(dipr);
dipr.diph.dwHeaderSize = sizeof(dipr);

Теперь вы задаете значение поля dipr.diph.dwObj, которое указывает для какой оси (X или Y) вы задаете диапазон значений, для чего применяются макросы DIJOFS_X и DIJOFS_Y соответственно. Давайте начнем с оси X, а затем перейдем к оси Y.

dipr.diph.dwObj = DIJOFS_X;
dipr.diph.dwHow = DIPH_BYOFFSET; // Смещение в формате данных

Первое свойство, которое вы устанавливаете, — диапазон значений для оси X. Устанавливаем минимальное и максимальное значение для диапазона от –1024 (крайняя левая позиция) до +1024 (крайняя правая позиция).

dipr.lMin = -1024;
dipr.lMax =  1024;

 

ПРИМЕЧАНИЕ
Теперь вы видите, что назначение структуры DIPROPRANGE — задание диапазонов; здесь мы задаем диапазон значений от –1024 до 1024, это значит, что использоваться будут любые значения, находящиеся между этими двумя числами.

Теперь для установки диапазона значений оси X вызываем функцию IDirectInputDevice8::SetProperty:

if(FAILED(g_pDIDJoystick->SetProperty(DIPROP_RANGE,
                                      &dipr.diph))) {
    g_pDIDJoystick->Release();
    g_pDIDJoystick = NULL;
    return DIENUM_CONTINUE;
}

Теперь установим диапазон значений для оси Y. Просто изменим значение dwObj (на DIJOFS_Y) и снова вызовем функцию установки свойства.

dipr.diph.dwObj = DIJOFS_Y;
if(FAILED(g_pDIDJoystick->SetProperty(DIPROP_RANGE,
                                      &dipr.diph))) {
    g_pDIDJoystick->Release();
    g_pDIDJoystick = NULL;
    return DIENUM_CONTINUE;
}

Ну вот! Осталось только установить диапазон мертвой зоны, чтобы небольшие перемещения джойстика не вызывали дрожания. Установка мертвой зоны слегка отличается от установки диапазонов осей, поскольку здесь вам надо указать единственное значение — процент, который занимает мертвая зона в диапазоне значений оси. Вы можете спокойно устанавливать мертвую зону, занимающую около 15 процентов диапазона; это значит, что пользователь должен отклонить рукоятку не менее чем на 15 процентов в любом направлении, чтобы перемещение было зафиксировано.

Чтобы установить размер мертвой зоны мы используем структуру DIPROPDWORD, которая во многом похожа на структуру DIPROPRANGE, и содержит структуру DIPROPHEADER и значение типа DWORD:

typedef struct DIPROPDWORD {
    DIPROPHEADER diph;   // Обсуждавшийся ранее заголовок свойства
    DWORD        dwData; // Значение DWORD для свойства
} DIPROPDWORD, *LPDIPROPDWORD;

Чтобы использовать структуру DIPROPDWORD для установки мертвой зоны, укажите в заголовке свойства, что работаете с осью X (воспользовавшись макросом DIJOFS_X), задайте процентное значение (в диапазоне от 0 для 0% до 10000 для 100%) и установите свойство с помощью вызова SetProperty:

dipdw.diph.dwObj = DIJOFS_X; // Ось X
dipdw.dwData     = 1500;     // Приблизительно 15% диапазона
if(FAILED(g_pDIDJoystick->SetProperty(DIPROP_DEADZONE,
                                      &dipdw.diph))) {
    g_pDIDJoystick->Release();
    g_pDIDJoystick = NULL;
    return DIENUM_CONTINUE;
}

 

ПРИМЕЧАНИЕ
Чтобы вычислить значение, которое надо указать в dipdw.dwData, умножьте желательный процент на 100. Например, 15% = 15 × 100 = 1500

Повторите то же самое для оси Y (используя макрос DIJOFS_Y):

dipdw.diph.dwObj = DIJOFS_Y; // Ось Y
if(FAILED(g_pDIDJoystick->SetProperty(DIPROP_DEADZONE,
                                      &dipdw.diph))) {
    g_pDIDJoystick->Release();
    g_pDIDJoystick = NULL;
    return DIENUM_CONTINUE;
}

Теперь устройство инициализировано, и пришло время захватить устройство и завершить перечисление:

    // Захватываем устройство для использования
    if(FAILED(g_pDIDJoystick->Acquire())) {
        g_pDIDJoystick->Release();
        g_pDIDJoystick = NULL;
        return DIENUM_CONTINUE;
    }
    // Останавливаем перечисление
    return DIENUM_STOP;
} // Конец функции

Теперь переменная g_pDIDJoystick содержит либо указатель на созданный объект, либо равна NULL, если джойстик не инициализирован. Если объект устройства был инициализирован, то вы можете читать информацию точно так же, как это делалось для клавиатуры и мыши, но используя в функции ReadData структуру DIJOYSTATE.Вот как выглядит структура DIJOYSTATE:

typedef struct DIJOYSTATE {
    LONG lX;  // Абсолютное значение координаты X 
    LONG lY;  // Абсолютное значение координаты Y 
    LONG lZ;  // Абсолютное значение координаты Z 
    LONG lRx; // Вращение по X
    LONG lRy; // Вращение по Y
    LONG lRz; // Вращение по Z
    LONG  rglSlider[2];   // Значения движков
    DWORD rgdwPOV[4];     // Значения POV 
    BYTE  rgbButtons[32]; // Флаги кнопок (для 32 кнопок)
} DIJOYSTATE, *LPDIJOYSTATE;

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

g_pDI->EnumDevices(DIDEVTYPE_JOYSTICK, EnumJoysticks,
                   NULL, DIEDFL_ATTACHEDONLY);
if(g_pDIDJoystick == NULL) {
    // Джойстик не инициализирован
}

Для чтения данных джойстика вызовите функцию ReadData со структурой DIJOYSTATE:

DIJOYSTATE JoystickState;
ReadData(g_pDIDJoystick, (void*)JoystickState, sizeof(DIJOYSTATE));

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

JoystickX = JoystickState.lX;
JoystickY = JoystickState.lY;

Кроме того, можно применять уже рассматривавшийся ранее макрос для чтения состояния кнопок:

#define JoystickButtonState(x) ((JoystickState.rgbButtons[x] & 0x80) ? TRUE : FALSE)

netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz