====== DirectInput ====== Es la parte de DirectX que interactua directamente con los dispositivos de entrada (teclado, ratón, joystick...), utilizándolo obtienes mayor flexibilidad en la obtención de datos introducidos por el usuario. Sus ventajas son que al tratarse directamente con el dispositivo se desvinculan a tu aplicación las configuraciones realizadas por el usuario en el sistema operativo, además no funciona por eventos sino que a cada momento del tiempo el dispositivo tiene un estado distinto. Seguiremos los siguientes pasos para una correcta utilización: - Se crea el objeto DirectInput (Objeto que gestiona el motor de DirectInput). - Se miran los dispositivos de entrada de los que se disponen. - Por cada dispositivo que queramos tratar creamos un objeto DirectInputDevice (Objeto que representa un dispositivo). - Configuramos cómo vamos a tratar el dispositivo. - Recogemos y tratamos los datos. - Cerramos DirectInput. \\ **Recuerda:** * Para poder programar con DInput necesitaremos linkar las siguientes librerías: ''dxguid.lib'' y ''dinput8.lib''. * Tenemos que incluir en nuestro .cpp el fichero //dinput.h// \\ Código general que se ha utilizado para los ejemplos: #include #define MSGBOX(body) MessageBox(NULL,body,"",MB_OK); ===== Utilizando DInput ===== ==== Creando el objeto DInput ==== Para crear un objeto utilizaremos la función: ''DirectInput8Create'', a esta se le pasan los siguientes argumentos: * El HINSTANCE de la aplicación que lo crea (parámetro que viene en el WinMain). * La flag //DIRECTINPUT_VERSION// que indica la versión de DInput. * Tipo de interface que ha de retornar, utilizaremos: //IID_IDirectInput8//. * Referencia al objeto //LPDIRECTINPUT8// que queremos crear. * NULL -> Es para compativilidades con objetos COM. LPDIRECTINPUT8 objDI; if (FAILED(DirectInput8Create(inst, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&objDI, NULL))) MSGBOX("Error al crear el objeto DInput"); ==== Crear dispositivos ==== === Crear objeto dispositivo === Para crear un objeto dispositivo tendremos que declarar primero un objeto de este tipo (//LPDIRECTINPUTDEVICE8//) y luego llamar a la función ''CreateDevice'' del objeto //LPDIRECTINPUT8// ya creado anteriormente. Esta función recoge los siguientes parámetros: * El identificador del dispositivos, podemos coger los "por defecto" que son: //GUID_SysKeyboard// y //GUID_SysMouse//. * Referencia al objeto //LPDIRECTINPUTDEVICE8// que se quiere crear. * NULL. === Cómo serán los datos? === Tenemos que asignar al dispositivo un formato de datos, es decir, indicar cómo vamos a tratar con él; tenemos los siguientes: ''c_dfDIJoystick'', ''c_dfDIJoystick2, c_dfDIKeyboard, c_dfDIMouse'', y ''c_dfDIMouse2''. Son variables globales que nos liberan del tedio de hacerlo por nuestra cuenta, sólo tenemos que pasar la elegida como referencia al método ''SetDataFormat'' del dispositivo que queramos. === Nivel cooperativo === Esto es cómo el dispositivo va a comunicarse con el Sistema Operativo, para indicarlo, en el objeto del dispositivo (//LPDIRECTINPUTDEVICE8//) llamamos al método ''SetCooperativeLevel'' pasándole: - El HWND de la aplicación. - Y dos de los siguientes flags: * //DISCL_NONEXCLUSIVE// o //DISCL_EXCLUSIVE//. * //DISCL_FOREGROUND// o //DISCL_BACKGROUND//. * Y si el dispositivo es un keyboard también podemos indicar que se desactive la tecla de windows pasándole otra flag: //DISCL_NOWINKEY// (en combinación con //DISCL_NONEXCLUSIVE//). \\ Básicamente con esto indicas si se seguirá el dispositivo cuando deje de estar en primer plano (foreground) o no; y si será exclusivo (que bloqueará el dispositivo para si mismo) o no. Podemos utilizar la siguiente combinación: * Para el teclado\ratón: //DISCL_FOREGROUND | DISCL_NONEXCLUSIVE//. Esta modalidad tiene el problema que cuando se puerde el foco de la ventana tendrás que volver a adquirir el dispositivo, en [[id=fw:dx:dinput#notas|notas]] tienes un ejemplo de como podría hacerse. * Para el ratón, si queremos manejar nosotros el cursor de windows (que desaparecerá): //DISCL_EXCLUSIVE | DISCL_FOREGROUND// === Adquirir el dispositivo === Esto es, como ya lo tenemos configurado se llama al método ''Acquire'' de este y podremos comenzar a utilizarlo. === Código === void createKeyboard () { objDI->CreateDevice(GUID_SysKeyboard, &keyb, NULL); keyb->SetDataFormat(&c_dfDIKeyboard); keyb->SetCooperativeLevel(hwnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE | DISCL_NOWINKEY); keyb->Acquire (); } void createMouse () { objDI->CreateDevice(GUID_SysMouse, &mouse, NULL); mouse->SetDataFormat(&c_dfDIMouse); mouse->SetCooperativeLevel(hwnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND); mouse->Acquire (); } ==== Cerrando DInput ==== Una vez acabemos de utilizar DInput debemos liberar los dispositivos (llamando a ''Unadquire'' y luego a ''Release'') y luego liberar el objeto DInput, llamando al ''Release'' de este: keyb->Unacquire(); keyb->Release(); mouse->Unacquire(); mouse->Release(); objDI->Release(); keyb = NULL; mouse = NULL; objDI = NULL; ==== Perdiendo el dispositivo ==== Una vez tengas funcionando tu aplicación, si esta pierde el foco (aunque sea por un messagebox propio) tendrás que volver a cargar el objeto dispositivo. Puedes detectar que la aplicación ha perdido el foco mediante el mensaje //WM_ACTIVATEAPP// al //WindowProcedure//. Sabrás si se ha activado\desactivado mediante el //wParam//. \\ Notarás, también, que has perdido algún dispositivo porque métodos de este ya no devolverán DI_OK. ===== Recoger datos... ===== ==== ... del teclado ==== Los datos del teclado vienen dados en un buffer donde se guarda el estado actual de cada tecla. Para recoger dicho buffer únicamente has de declarar un array de bytes de 256 (nº de teclas) y llamar a la función ''GetDeviceState'' del objeto dispositivo pasándole como primer parámetro el tamaño del buffer (256 o ''sizeof(buffer)''). \\ Para saber si se ha presionado la tecla que queremos existen unas constantes de DInput con los nombres de las teclas (DIK_ESCAPE, DIK_LEFT...), son números que corresponden a una posición del buffer. Para saber si una tecla en concreto se ha pulsado el contenido en el buffer de la posición correspondiente añadiendole lógicamente (&) un valor ''0x80'' tiene que dar como valor booleano ''true''. #define KEYDOWN(name, key) (name[key] & 0x80) ... byte buffer[256]; HRESULT hr = keyb->GetDeviceState(sizeof(buffer), (LPVOID)&buffer); if (hr == DI_OK) { if (KEYDOWN(buffer, DIK_ESCAPE)) ... if (KEYDOWN(buffer, DIK_LEFT)) ... } ==== ... del mouse ==== Para controlar la posición del mouse necesitarás de una nueva estructura: //DIMOUSESTATE//. Un objeto de esta es lo que has de pasar al ''GetDeviceState'' del objeto dispositivo del mouse junto con un sizeof: DIMOUSESTATE msdata; mouse->GetDeviceState(sizeof(msdata), &msdata); Las propiedades que contiene son: ''lX'' e ''lY''. En ellas encontrarás cuanto se ha movido el mouse en esas coordenadas desde la última vez que se hizo un ''GetDeviceState''. También contiene un array de bytes con 4 posiciones correspondientes a 4 botones del ratón, para saber si se ha pulsado algún botón sólo has de añadir lógicamente (&) el valor 0x80 a la posición de dicho array correspondiente al botón (izquierdo = 0, derecho = 1...) y te dará un valor booleano que indica si ha sido pulsado o no. Otro valor, el ''lZ'', indica cuanto ha sido movida la rueda. DIMOUSESTATE msdata; mouse->GetDeviceState(sizeof(msdata), &msdata); mouseX += msdata.lX; mouseY += msdata.lY; if (msdata.rgbButtons[0] & 0x80) ... if (msdata.lZ > 0) ... ===== Notas ===== * Cuando llamamos a funciones de DInput estas han de retornar //DI_OK// si todo ha salido bien. Aún así, y para una sencilla comprobación, los programadores de DirectX nos proporcionan una macro llamada FAILED que indica si ha fallado la acción: if (FAILED(DirectInput8Create(inst, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&objDI, NULL))) MSGBOX("Error al crear el objeto DInput"); * Para volver a adquirir el dispositivo si se pierde el foco... byte buffer[256]; HRESULT hr = this->keyb->GetDeviceState(sizeof(buffer), (LPVOID)&buffer); if (hr == DI_OK) return KEYDOWN(buffer, key); this->keyb->Acquire();