Сегодня мы рассмотрим программу использующую как Direct3D, так и DirectInput. При запуске программы на экран выводится чёрный квадрат. Используя стрелочки клавиатуры квадрат можно перемещать.

В прошлый раз мы рассмотрели инициализацию DirectInput. К этому моменту у нас уже есть переменная в которой хранится адрес интерфейса устройства IDirectInputDevice8 - didev, и мы получили доступ к устройству ввода (клавиатуре) через метод IDirectInputDevice8::Acquire.

DirectInput позволяет получить асинхронный доступ к устройствам (прежде всего к клавиатуре). Причём доступ к устройству осуществляется очень быстро. Что значит асинхронный доступ? Это значит, что программа не ждёт когда пользователь что-нибудь введёт. При использовании синхронного доступа, программа останавливается и ждёт ввода от пользователя. Использовать синхронный доступ в играх ни в коем случае нельзя.

Асинхронный доступ работает следующим образом: во время итерации основного цикла программы, когда очередь доходит до ввода, делаются "снимки" состояния нужных устройств. Например, для клавиатуры, не проверяется каждая конкретная клавиша, а всё состояние клавиатуры сохраняется в буфер. А уже в этом буфере программист может проверить состояния конкретных клавиш.

DirectInput знает, что мы будем запрашивать состояние всей клавиатуры и сохранять его в массиве из 256-и байт. Мы сообщили об этом, когда устанавливали формат данных IDirectInputDevice8::SetDataFormat. Аргумент этого метода - структурная переменная c_dfDIKeyboard, как раз и содержал эту информацию.

Опросить клавиатуру можно с помощью метода IDirectInputDevice8::GetDeviceState. Первый аргумент данного метода - размер буфера, второй аргумент - адрес буфера. В результате работы метода в буфер будет сохранено текущее состояние клавиатуры.

Клавиши клавиатуры представлены в виде констант: DIK_A - DIK_Z (от A до Z), DIK_SPACE - пробел; DIK_LEFT, DIK_RIGHT, DIK_UP, DIK_DOWN - стрелочки; DIK_ESCAPE - ескейп.

Клавиша нажата, когда старший бит соответствующего элемента массива установлен в единицу. Для проверки нажатия клавиши используется следующий код:

код на языке c++
if (buffer[DIK_ESCAPE] | 0x80)
{
  // была нажата клавиша Escape
}

О поразрядных логических операциях можно узнать в статье Операции C++.

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

Каждую итерацию цикла проверяется состояние клавиатуры. Затем в массиве, в котором хранится последний снимок клавиатуры, мы проверяем нажатие пользователем стрелочек. Если была нажата какая-то стрелочка, то мы изменяем на единицу одну из переменных dx или dy.

Определения переменных dx, dy и буфера для состояния клавиатуры расположены внутри WinMain, до начала основного цикла:

int dx=0,dy=0;
char buffer[256];

Обратите внимания, что буфер для хранения состояния клавиатуры - обычный массив типа char из 256-и элементов.

Весь оставшийся код располагается в основном цикле:

dx = 0;
dy = 0;

Переменные dx и dy каждую итерацию цикла устанавливаются в ноль. Это необходимо, чтобы квадрат передвигался не более чем на одну единицу измерения за итерацию цикла.

код на языке c++
hr = didev->GetDeviceState(sizeof(buffer),buffer); 
if FAILED(hr) 
  return 0;

Здесь непосредственно происходит съём состояния клавиатуры в массив buffer. Метод GetDeviceState возвращает значение HRESULT. Мы его проверяем в следующей строке используя макрос FAILED. Если съём состояния клавиатуры произвести не удалось, то программа завершается. Зачем я это сделал? Если вы нажмёте мышкой на рабочем столе, то программа завершится. Я сделал это для того, чтобы не возиться с получением доступа к устройству каждый раз, когда доступ теряется (пользователь выбрал другую программу, а потом вернулся). По идее, здесь нужно организовать бесконечный цикл, и при каждой итерации пытаться получить доступ к устройству (Acquire).

Далее идёт непосредственно проверка нажатия клавиш стрелочек и изменение переменных dx и dy:

код на языке c++
if (buffer[DIK_RIGHT] & 0x80) 
  dx += 1; 
else if(buffer[DIK_LEFT] & 0x80) 
  dx -= 1;

if (buffer[DIK_UP])
  dy+=1;
else if (buffer[DIK_DOWN])
  dy-=1;

И последняя часть интересующего нас кода:

код на языке c++
for (int i = 0; i < 6; i++)
{
  vertices[i].x += dx;
  vertices[i].y += dy;
}

Здесь мы пробегаем по всем вершинам квадрата (их шесть - квадрат состоит из двух треугольников) и меняем поля x и y этих вершин на dx/dy.

На этом всё. Посмотреть весь код программы можно здесь. Программа нормально компилируется и запускается. Только незабудьте добавить к проекту библиотечные файлы: d3d9.lib, dinput8.lib, dxguid.lib. Каждый файл записывается через пробел.