DirectInput мышь
Дата создания: 2009-09-01 17:13:25
Последний раз редактировалось: 2012-02-08 10:57:07
- Предварительные уроки:
- Инициализация DirectInput. Перейти.
- Ввод с клавиатуры в DirectInput. Перейти.
- Файлы исходников к уроку:
- Немедленный режим ввода. Перейти.
В сегодняшнем уроке мы рассмотрим получение данных ввода с устройства мышь, используя DirectInput.
Сразу рассмотрим код инициализации DirectInput для мышки:
IDirectInput8* di; // объект DirectInput IDirectInputDevice8* didev; // устройство DirectInput DirectInput8Create(hInstance, // экземпляр приложения DIRECTINPUT_VERSION, // версия DirectInput IID_IDirectInput8, // глобальный идентификатор (COM) (void**)&di, // адрес указателя объекта DirectInput NULL); di->CreateDevice(GUID_SysMouse, // устройство ввода с мыши &didev, // адрес указателя на устройство NULL); didev->SetDataFormat(&c_dfDIMouse); didev->SetCooperativeLevel(hWnd,DISCL_FOREGROUND | DISCL_NONEXCLUSIVE); didev->Acquire();
Различия по сравнению с инициализацией DirectInput для клавиатуры заключаются в передаваемых аргументах методов IDirectInput8::CreateDevice и IDirectInputDevice8::SetDataFormat.
Для перехвата ввода с мышки, также как и для перехвата ввода с клавиатуры, необходимо, чтобы был получен доступ к устройству - методом IDirectInputDevice8::Acquire. Поэтому если наша программа теряет фокус, а потом снова становится активной, мы должны предусмотреть подобную ситуацию
В DirectInput существует два способа получить ввод мыши. Здесь мы рассмотри только один:
Немедленный режим (immediate mode) ввода данных с мыши в DirectInput
Как вы, наверное, помните, ввод с клавиатуры мы получали с помощью метода IDirectInputDevice8::GetDeviceState. Этот же метод используется и для получения ввода с мышки. Давайте посмотрим на определение:
HRESULT GetDeviceState( DWORD cbData, LPVOID lpvData );
Get device state переводится как - получить состояние устройства.
cpData
Размер буфера, в который будет сохранено текущее состояние устройства.
lpvData
Адрес буфера.
Итак, в этот метод передаётся адрес буфера, в котором будет сохранено текущее состояние устройства ввода, и размер этого буфера. Для клавиатуры мы создавали простой массив из 256-и элементов. Для мышки в DirectInput используется специальная структура - DIMOUSESTATE (Direct Input Mouse State - состояние мышки в DirectInput). Поэтому нам нужно объявить соответствующую переменную до основного цикла (и обнулить все её поля):
DIMOUSESTATE dims; ZeroMemory(&dims,sizeof(dims));
Теперь посмотрим, из чего состоит эта структура:
typedef struct DIMOUSESTATE { LONG lX; LONG lY; LONG lZ; BYTE rgbButtons[4]; } DIMOUSESTATE, *LPDIMOUSESTATE;
Как видите, в структуре хранится перемещение мышки по трём осям (обратите внимание на ось z, которая отвечает за прокрутку колёсика мышки) и 4 кнопки (массив rgbButtons):
rgbButtons[0] - левая кнопка мыши.
rgbButtons[1] - правая кнопка мыши.
rgbButtons[2] - колёсико мыши.
Вот в общем-то и всё. Теперь мы можем очень легко взять нужную нам информацию из структуры DIMOUSESTATE.
Код довольно простой (если вы читали урок о вводе с клавиатуры в DirectInput). Думаю, вы сами разберётесь:
didev->GetDeviceState(sizeof(DIMOUSESTATE),&dims); dx = dims.lX; dy = dims.lY; dz = dims.lZ; if (dims.rgbButtons[0] & 0x80) { for (int i = 0; i < 6; i++) vertices[i].color = 0xffff0000; } if (dims.rgbButtons[1] & 0x80) { for (int i = 0; i < 6; i++) vertices[i].color = 0xff00ff00; } if (dims.rgbButtons[2] & 0x80) { for (int i = 0; i < 6; i++) vertices[i].color = 0xff0000ff; } for (int i = 0; i < 6; i++) { vertices[i].x += dx; vertices[i].y -= dy; } matWorld._43 += dz/10; dev->SetTransform(D3DTS_WORLD, &matWorld);
Сначала мы получаем текущее состояние мышки.
После этого идёт проверка на нажатие клавиш мышки, и если была нажата одна из кнопок, мы перекрашиваем все вершины в соответствующий цвет: левая кнопка мышки - красный, правая кнопка мыши - зелёный, колёсико мышки - синий.
Затем идёт код перемещения всех вершин квадрата с помощью полей lX и lY. Далее мы приближаем или удаляем квадрат к камере, изменяя элемент _43 (четвёртая строка, третий столбец) матрицы matWorld на значение lZ (прокрутка колёсика мышки). При этом поле lZ мы делим на 10. Это необходимо, чтобы квадрат не приближался/удалялся слишком быстро. В конце мы передаём изменившуюся матрицу matWorld в метод SetTransform.
Теперь выясним почему данный способ получения ввода мыши называется - немедленным (immediate mode). DirectInput получает информацию о состоянии мыши двенадцать раз в секунду. Обратите внимание на наш код: мы опрашиваем устройство с помощью метода IDirectInputDevice8::GetDeviceState, помещаем состояние устройства в структурную переменную dims, ну а затем смотрим, какие поля данной переменной нам нужны.
И всё бы хорошо, но представим ситуацию, когда у нас получается выводить на экран только один кадр в секунду (допустим, нам нужно построить очень сложную сцену). А это значит, что за секунду мы успеем выполнить только одну итерацию основного цикла, что в свою очередь означает только один вызов метода IDirectInputDevice8::GetDeviceState за секунду. Это приведёт к тому, что мы потеряем одиннадцать промежуточных состояний устройства (мышки).
Собственно, использование только метода IDirectInputDevice8::GetDeviceState и есть этот самый немедленный режим ввода данных с устройства мышь. Т.е. мы получаем состояние устройства в какой-то конкретный момент времени, при этом возможна потеря промежуточных состояний (если метод GetDeviceState вызывается меньше двенадцати раз в секунду).
Чтобы получить все данные с устройства (все состояния) используется буферизованный режим. При этом используется метод IDirectInputDevice8::GetDeviceData.
Буферизованный режим ввода данных с мышки мы рассмотрим позднее.
Упражнения
1. Запустите программу, выберите другое окно и снова вернитесь к программе. Понаблюдайте за курсором и квадратом. Затем поменяйте константу DISCL_FOREGROUND в методе IDirectInputDevice8::SetCooperativeLevel на DISCL_BACKGROUND. Выберите другое окно и снова вернитесь в программу.
2. Поменяйте константу DISCL_NONEXCLUSIVE в методе IDirectInputDevice8::SetCooperativeLevel на DISCL_EXCLUSIVE, а константу DISCL_BACKGROUND на DISCL_FOREGROUND. Запустите программу. Попробуйте выбрать другое окно (например, с помощью Alt+Tab) и снова вернуться к программе. Программу можно закрыть комбинацией клавиш Alt+F4.
3. Перепешите программу так, чтобы в режиме взаимодействия с операционной системой DISCL_FOREGROUND, при потере фокуса и его возвращении, квадрат продолжал двигаться.
4. Перепишите код программы таким образом, чтобы перемещение квадрата осуществлялось с помощью матрицы matWorld.