22 февраля 2010 в 21:14
Ввод с клавиатуры в DirectInput
Сегодня мы рассмотрим программу использующую как 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 - ескейп.
Клавиша нажата, когда старший бит соответствующего элемента массива установлен в единицу. Для проверки нажатия клавиши используется следующий код:
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 каждую итерацию цикла устанавливаются в ноль. Это необходимо, чтобы квадрат передвигался не более чем на одну единицу измерения за итерацию цикла.
hr = didev->GetDeviceState(sizeof(buffer),buffer); if FAILED(hr) return 0;
Здесь непосредственно происходит съём состояния клавиатуры в массив buffer. Метод GetDeviceState возвращает значение HRESULT. Мы его проверяем в следующей строке используя макрос FAILED. Если съём состояния клавиатуры произвести не удалось, то программа завершается. Зачем я это сделал? Если вы нажмёте мышкой на рабочем столе, то программа завершится. Я сделал это для того, чтобы не возиться с получением доступа к устройству каждый раз, когда доступ теряется (пользователь выбрал другую программу, а потом вернулся). По идее, здесь нужно организовать бесконечный цикл, и при каждой итерации пытаться получить доступ к устройству (Acquire).
Далее идёт непосредственно проверка нажатия клавиш стрелочек и изменение переменных dx и dy:
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;
И последняя часть интересующего нас кода:
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. Каждый файл записывается через пробел.
28 мая 2010 в 23:55
В прошлом уроке такого не было. Дайте хоть листинг посмотреть :)
29 мая 2010 в 05:28
вот прошлый урок Инициализация Direct Input
21 октября 2010 в 07:09
У меня на Windows7x64 с Microsoft DirectX SDK (June 2010) выдавало ошибку пока не добавил:
#define DIRECTINPUT_VERSION 0x0800
28 июня 2013 в 10:40
Выдавало ошибку, пока не добавил #define INITGUID. Но фигура не двигается при нажатии стрелок
авторизуйтесь
или войдите через