Manejo básico de OIS

De IberOgre

Imagino que llegados a este punto estaréis al corriente de que Ogre no es un motor para desarrollar videojuegos en 3D, simplemente es un motor de renderizado. Esto implica que debemos recurrir a otras librerías para disponer de un gestor de entrada de eventos o de reproducción de sonido por citar algún ejemplo. En esta ocasión vamos a dedicar un hueco en IberOgre para desgranar el funcionamiento básico de OIS, el gestor de entrada más utilizado en el mundo del desarrollo con Ogre.

Al utilizar OIS en IberOgre se hacen necesarias unas directrices básicas en cuanto a su uso. Este artículo no pretende convertir al lector en todo un experto en la materia aunque sí podrá proveerlo de los conocimientos básicos para continuar.


Contenido


Requisitos previos

El uso de OIS no tiene porqué estar ligado completamente al de Ogre3D por ello, los requisitos en este caso pueden ser más flexibles. No obstante, antes de seguir adelante, se recomienda:

  • Instalación: los artículos "Instalación de Ogre3D 1.7 en Windows" e "Instalación de Ogre3D 1.7 en GNU/Linux" incluyen la instalación de OIS por lo que, sino la has instalado por tu cuenta, deberías acudir a ellos en primer lugar.
  • Inicialización y cierre de Ogre: los eventos están íntimamente ligados a la ventana de la aplicación en el que tienen lugar. Para capturar eventos con OIS (y seguramente con otras librerías) necesitas tener una ventana de Ogre3D funcionando. Es imprescindible que sepas cómo hacer eso antes de leer este artículo.


Información general sobre OIS

OIS (Object Oriented Input System) es una librería para gestionar una gran variedad de dispositivos de entrada entre los que se encuentran ratones, teclados o joysticks. OIS es una biblioteca multiplataforma que soporta GNU/Linux, Windows y Mac OS (parcialmente). Podemos utilizarla con tranquilidad ya que está liberada bajo zlib/libpng, se trata de una licencia extraña, no obstante es sencilla. Guarda numerosas similitudes con la LGPL, podemos comercializar proyectos que la utilicen o modificar su código y no estar obligados a distribuirlo.

OIS es la biblioteca de gestión de dispositivos de entrada más popular entre la comunidad oficial de Ogre3D lo cual nos aporta diversas ventajas. Entre ellas, la más evidente es el soporte de los usuarios en foros y otros medios. A continuación, algunos enlaces de interés:

  • Web: Blog oficial
  • Código: SourceForge
  • Documentación: es necesario descargar el código y generarla con Doxygen.


Inicialización y cierre de OIS

En esta sección aprenderemos a iniciar y configurar OIS así como cerrarla correctamente.

Inicialización de OIS

Con Ogre3D creamos la ventana de nuestra aplicación y comenzamos a renderizar modelos en ella. Nuestra intención al utilizar OIS es poder capturar los eventos de dispositivos de entrada para procesarlos y darles una respuesta. Al ser componentes distintos la librería no conoce inicialmente la ventana sobre la que deseamos capturar dichos eventos. Puede que estemos pulsando teclas pero, a menos que tengamos seleccionada la ventana de nuestra aplicación, no deberíamos capturar tales pulsaciones.

Lo primero que debemos hacer es indicarle a OIS cual es la ventana a la que debe prestar atención. Si lo deseamos, podemos pasarle opciones adicionales de forma que el comportamiento obtenido sea el deseado. A continuación se muestran las dos posibilidades disponibles a la hora de crear el gestor de entrada de OIS, para más detalles aconsejo consultar la documentación de OIS, la cual está generada con Doxygen:

static InputManager* OIS::InputManager::createInputSystem(std::size_t winHandle)
static InputManager* OIS::InputManager::createInputSystem(ParamList &paramList)
  • size_t winHandle: parámetro con información específica de la plataforma relacionada con la ventana (X11, HWND etc)
  • ParamList &paramList: equivalente a un clásico std::map<string, string> que contiene una relación de parámetros para configurar el comportamiento de OIS.

Es cierto que para el primer método nos basta con conocer el manejador de la ventana de nuestra aplicación. No obstante, como veremos más adelante, es ampliamente probable que deseemos configurar aún más la captura de eventos. Para ello necesitamos el diccionario de parámetros y, por tanto, el segundo método.

Suponemos que disponemos de mVentana, un puntero a Ogre::RenderWindow inicializado con nuestra ventana. La clase Ogre::RenderWindow dispone de un método capaz de ofrecer información específica de la plataforma usada. Los propios desarrolladores afirman que es "horrendo" en su propia documentación, no obstante, es la manera utilizada para indicarle a OIS el tipo de ventana que usamos. El método es el siguiente:

Ogre::RenderTarget::getCustomAttribute(const String &name, void *pData)

Al indicarle que buscamos información sobre "WINDOW", nos insertará en el segundo parámetro la información que buscamos. Es el momento de volcar la información de la ventana en una cadena de la STL para insertarla en el diccionario de parámetros. El siguiente ejemplo resume el proceso elemental para iniciar OIS de forma correcta.

// Diccionario de parámetros para la inicialización de OIS
OIS::ParamList parametrosOIS;
// Handler (manejador) de nuestra ventana
size_t ventanaHnd = 0;
// Flujo para almacenar la información de nuestra ventana
std::ostringstream ventanaHndStr;
 
// Tomamos el manejador de la ventana
mVentana->getCustomAttribute("WINDOW", &ventanaHnd);
 
// Lo convertimos a flujo
ventanaHndStr << ventanaHnd;
 
// Insertamos el parámetro en el diccionario
parametrosOIS.insert(std::make_pair(std::string("WINDOW"), ventanaHndStr.str()));
 
// Creamos el gestor de entrada con los parámetros
OIS::InputManager* mGestorEntrada = OIS::InputManager::createInputSystem(parametrosOIS);

Bien, hemos conseguido inicializar OIS con éxito pero aún no podemos leer el estado de ningún dispositivo. OIS dispone de una clase OIS::Object para manejar los sistemas de entrada de forma uniforme. Concretamente, existen 3 tipos de dispositivos en OIS:

  • Keyboard: cualquier tipo de teclado.
  • Mouse: un ratón convencional.
  • Joystick: tipo de dispositivo que engloba palancas de control o mandos más convencionales.

Para inicializar el control sobre uno de estos dispositivos en OIS disponemos del siguiente método:

OIS::Object* OIS::InputManager::createInputObject(Type iType, bool bufferMode, const std::string & vendor = "")

El método está pobremente comentado incluso en la documentación oficial aunque tampoco necesitamos saber demasiado. Para obtener el controlador de un dispositivo llamamos a este método con el tipo de dispositivo que deseemos obtener y seleccionamos el bufferMode. Obtendremos un dispositivo genérico OIS que precisará de una conversión. Partiendo del ejemplo básico anterior podemos añadir el siguiente fragmento de código con el fin de disponer de teclado, ratón y joystick:

// Creamos los manejadores para el teclado, el ratón y un joystick
mTeclado = static_cast<OIS::Keyboard*>(mGestorEntrada->createInputObject(OIS::OISKeyboard, true));
mRaton = static_cast<OIS::Mouse*>(mGestorEntrada->createInputObject(OIS::OISMouse, true));
mJoystick = static_cast<OIS::JoyStick*>(mGestorEntrada->createInputObject(OIS::OISJoyStick, true));

Configurando OIS::InputManager

Lo más común será configurar el InputManager de OIS para que se comporte de la manera que buscamos. Es probable que queramos que sólo detecte eventos dentro de nuestra ventana pero que los externos sigan funcionando correctamente. Si no establecemos las siguientes opciones es probable que los eventos externos a nuestra aplicación dejen de detectarse, que no podamos trabajar con nada más.

Por supuesto, queremos que se puedan hacer más tareas mientras se ejecuta nuestro juego. Para ello tenemos que emplear los siguientes parámetros. Son específicos de la plataforma, en IberOgre pretendemos desarrollar videojuegos multiplataforma (al menos para GNU/Linux y Windows) por lo que tenemos que atender las dos posibilidades:

#if defined OIS_WIN32_PLATFORM
parametrosOIS.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_FOREGROUND" )));
parametrosOIS.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_NONEXCLUSIVE")));
parametrosOIS.insert(std::make_pair(std::string("w32_keyboard"), std::string("DISCL_FOREGROUND")));
parametrosOIS.insert(std::make_pair(std::string("w32_keyboard"), std::string("DISCL_NONEXCLUSIVE")));
#elif defined OIS_LINUX_PLATFORM
parametrosOIS.insert(std::make_pair(std::string("x11_mouse_grab"), std::string("false")));
parametrosOIS.insert(std::make_pair(std::string("x11_mouse_hide"), std::string("false")));
parametrosOIS.insert(std::make_pair(std::string("x11_keyboard_grab"), std::string("false")));
parametrosOIS.insert(std::make_pair(std::string("XAutoRepeatOn"), std::string("true")));
#endif

Se entiende que estos parámetros han de establecerse antes de la llamada a:

OIS::InputManager* mGestorEntrada = OIS::InputManager::createInputSystem(parametrosOIS);

Cierre de OIS

Cuando cerramos nuestra aplicación lo correcto es liberar los recursos requeridos por el sistema. En esta ocasión será necesario destruir los manejadores de dispositivos específicos y el gestor de entrada. El orden es importante, en primer lugar deben ser destruidos los dispositivos de manera que el último paso sea destruir el gestor de entrada. A tal efecto usaremos los siguientes métodos:

void OIS::InputManager::destroyInputObject(Object *obj);
static void OIS::InputManager::destroyInputSystem(InputManager *manager);

Un buen ejemplo continuando con todo el código anterior sería:

mGestorEntrada->destroyInputObject(mTeclado);
mGestorEntrada->destroyInputObject(mRaton);
mGestorEntrada->destroyInputObject(mJoystick);
OIS::InputManager::destroyInputSystem(mGestorEntrada);


Manejando dispositivos

En este apartado ofreceremos los métodos básicos para actualizar y consultar el estado de los manejadores de dispositivos específicos. Por supuesto, están provistos de más funcionalidades que escapan al objetivo de la presente guía. Si se pretende profundizar, aconsejo acudir a la documentación oficial.

Teclado

Teclado especial para jugadores

Entre la multitud de posibilidades que ofrece OIS para el teclado es muy probable que solamente nos interese un subconjunto de ellas. La operación más común que realizaremos con el manejador del teclado será consultar si una tecla está pulsada para lo que es necesario actualizar su estado cada frame. Al comienzo de cada bucle de juego o "game loop" hay que emplear el siguiente método:

void Keyboard::capture();

En nuestro caso:

mTeclado->capture();

Una vez hecho esto el estado interno de nuestro objeto "OIS::Keyboard" se actualizará con todos los eventos relacionados con el teclado. OIS distingue entre dos tipos de teclas: las normales y los modificadores. Los modificadores se refieren a las teclas tipo "Alt" o "Ctrl" mientras que el resto de teclas están englobadas en la categoría general.

Los modificadores están representados por el enumerado "OIS::Modifier" que tiene los siguientes valores:

  • Shift
  • Ctrl
  • Alt

Para consultar si un modificador está pulsado en un momento dado disponemos del método:

bool OIS::Keyboard::isModifierDown(Modifier mod);

Un ejemplo sencillo de su uso sería:

if (mTeclado->isModifierDown(OIS::Keyboard::Modifier::Shift)) {
    // Haz lo que tengas que hacer si Shift está pulsado
}

En cambio, el resto de teclas se encuentran en el enumerado "OIS::KeyCode". La lista de valores es enorme ya que mapea un teclado común al completo, incluso están cubiertas teclas como "apagar" o "entrar en suspensión". A continuación se lista una muestra reducida, el resto pueden consultarse en la documentación oficial. Una regla nemotécnica nos vendría bien en una ocasión como está. Básicamente los valores del enumerado se componen de "KC_" seguido de la tecla en cuestión en mayúscula.

  • KC_1
  • KC_2
  • KC_3
  • ...
  • KC_Q
  • KC_W
  • KC_E
  • KC_R
  • KC_T
  • KC_Y
  • ...
  • KC_BACKSLASH
  • ...

A la hora de consultar si una tecla común (KeyCode) está presionada utilizaremos el siguiente método:

bool OIS::Keyboard::isKeyDown(KeyCodekey);

Es muy sencillo aunque, para aclarar su uso se muestra un ejemplo:

if (mTeclado->isKeyDown(OIS::Keyboard::KeyCode::KC_SPACE)) {
    // Hacer algo, ¡se ha pulsado la barra espaciadora!
}

Ratón

Ratón de alta precisión

Dependiendo del tipo de juego que estemos desarrollando es posible que dependamos más o menos del ratón, el dispositivo por excelencia, que diferencia al PC de las consolas. Lo normal es que debamos conocer su posición, el estado de sus botones y, quizás, el scroll. Como en el caso anterior, será necesario actualizar el estado del ratón en cada iteración del bucle de juego:

void Mouse::capture();

En nuestro caso:

mRaton->capture();

Una vez realizada esta operación idéntica en los tres tipos de dispositivos debemos descubrir la manera de consultar el estado del ratón. Para ello, disponemos de un método que nos devuelve un objeto del tipo "OIS::MouseState" con dicha información.

const MouseState& OIS::Mouse::getMouseState() const;

Los botones del ratón están representados mediante un enumerado llamado "OIS::MouseButtonID". Cuenta con numerosos botones de los que algunos ratones estarán desprovistos, no obstante, listaremos todos ellos:

  • MB_Left
  • MB_Right
  • MB_Middle
  • MB_Button3
  • MB_Button4
  • MB_Button5
  • MB_Button6
  • MB_Button7

La clase "OIS::MouseState" dispone de un método para recuperar el estado de cada uno de los botones y consta de la siguiente signatura:

bool OIS::MouseState::buttonDown(MouseButtonID button) const;

Un sencillo ejemplo de su uso sería el siguiente:

if (mRaton->getMouseState()->buttonDown(OIS::MouseButtonID::MB_Rigth)) {
    // Botón derecho del ratón pulsado, quizás debas mostrar un menú
}

No podemos olvidarnos de la posición del ratón, OIS guarda información relativa a 3 ejes: X, Y, Z. Es evidente que los componentes X e Y representan el movimiento del ratón en el plano pero, ¿a qué se refiere el eje Z? En OIS se refiere a la rueda del ratón, de hecho, si ésta se puede pulsar, también será el botón central del mismo. En OIS tenemos la clase "OIS::Axis" para manejar los ejes, accedemos a los mencionados ejes mediante los siguientes atributos públicos de la clase "OIS::MouseState":

  • Axis X: eje x del ratón (horizontal).
  • Axis Y: eje y del ratón (vertical).
  • Axis Z: eje z del ratón (la rueda).

De "OIS::Axis" únicamente nos interesan 3 atributos públicos:

  • int rel: valor relativo del eje con respecto a la pantalla. Si la pantalla se redimensiona durante el juego hay que tener cuidado con su valor.
  • int abs: valor absoluto del eje.
  • bool absOnly: booleano que nos indica si únicamente es válido el campo "abs". Esto suele ocurrir en Joysticks (palancas de juego), donde sólo pueden tomarse valores absolutos.

En los últimos párrafos hemos hablado de varias clases y tipos de datos nuevos, es perfectamente comprensible que alguien se haya perdido entre tantos nombres y conceptos. Estoy seguro de que mostrando un sencillo ejemplo podremos manejar la posición del ratón con soltura:

// Obtener valor relativo con respecto a la pantalla de la posición en el eje X del ratón
mRaton->getMouseState()->X.rel;
 
// Obtener valor absoluto de la posición en el eje Y del ratón
mRaton->getMouseState()->X.abs;
 
if (mRaton->getMouseState()->Z.absOnly) {
    // En este caso el eje Z del ratón sólo toma valores absolutos
}

Joystick

Joystick para simuladores

Las palancas de control tuvieron su época dorada hace varios años en el PC aunque aún hoy día se siguen utilizando para simuladores de vuelo, por ejemplo. No obstante, en esta categoría también se engloban pads de control, muy utilizados en juegos deportivos o "hack'n slash". Al igual que con los dos dispositivos anteriores, debemos actualizar su estado interno en cada paso del "game loop":

mJoystick->capture();

La clase "OIS::Joystick" está provista de varios métodos para consultar los componentes disponibles del dispositivo conectado. Este tipo de controles suelen ser muy variados y es posible que precisemos determinar con cuántos ejes o botones podemos contar. Para consultar dichos métodos aconsejo acudir a la documentación oficial, en esta guía nos centraremos en explicar cómo consultar su estado interno. Al igual que con el ratón, accedemos a dicho estado a través del siguiente método:

const JoyStickState& getJoyStickState() const;

La clase "OIS::JoyStickState" es algo más incompleta que su equivalente en el ratón pero igualmente podemos trabajar con ella. Consta de dos atributos públicos reseñables:

  • std::vector<bool> mButtons: array de booleanos con la información de sus botones. Si una posición determinada está a "true" significa que dicho botón está pulsado, "false" en caso contrario.
  • std::vector<Axis> mAxes: array con el estado interno de cada eje. Como mencionamos anteriormente, es probable que sólo sea válido el campo absoluto. Los valores absolutos oscilan entre "OIS::JoyStick::MIN_AXIS" y "OIS::JoyStick::MAX_AXIS"

Como final de la explicación de los Joysticks en OIS presento un sencillo ejemplo para consultar el estado del mismo:

// Ejes del Joystick
if (mJoystick->getJoyStickState()->mAxes[X].abs < OIS::JoyStick::MAX_AXIS) {
    // El eje X del JoyStick no tiene su máximo valor
}
 
// Botones del Joystick
if (mJoystick->getJoyStickState()->mButtons[0]) {
    // El botón 0 del Joystick está presionado
}

Listeners de eventos

En los apartados anteriores se ha explicado el proceso para poder iniciar y cerrar OIS así como las instrucciones pertinentes para acceder al estado de botones y ejes de los dispositivos necesarios. Un método perfectamente válido, no obstante, es posible que necesitemos ser avisados de determinados eventos en el momento en el que se produzcan, sin que los consultemos explícitamente. Eso es exactamente lo que conseguimos con los "Listeners".

¿Qué es un listener?

Un "Listener", también conocido como "Observer", es un patrón de diseño bastante utilizado en multitud de situaciones. Supongo que estás familiarizado con el concepto de "patrones de diseño", si no es así deberías consultar información adicional sobre el tema. En resumen, son soluciones génericas para un tipo de problema de diseño.

El patrón Observador define una dependencia uno-varios entre objetos de forma que cuando el estado de uno cambia los dependientes son notificados. Es una solución que se aplica a la perfección a nuestro contexto. Imaginad que tenemos una clase por cada estado en nuestro juego, cuando el estado del teclado cambie (se pulsa una tecla), nos gustaría que el objeto con el estado actual del juego fuera notificado y actuase en consecuencia. Es algo similar a lo que ocurre en el desarrollo de aplicaciones de escritorio (GTK, QT...), la clase de vista captura los eventos que deseemos y les damos respuesta mediante manejadores de eventos ("event handlers").

En OIS los "Listeners" son sencillos de manejar y se tratan de forma más o menos independiente según dispositivo en cuestión. No obstante, el proceso entraña una mayor complejidad que los apartados anteriores, es recomendable dominarlos con soltura antes de continuar. Lo usual es que la clase encargada de capturar y manejar los eventos herede del "Listener" correspondiente e implemente los manejadores de eventos asociados. Como habréis podido imaginar, OIS tiene 3 tipos de "Listeners", uno por cada dispositivo (ratón, teclado y joystick).

OIS::KeyListener

"OIS::KeyListener" es el observador de eventos del teclado en OIS. Simplemente debemos tener una clase que herede de forma pública de esta interfaz y que implemente los manejadores de eventos correspondientes. En este caso, los manejadores corresponden a la pulsación y a la liberación de teclas. El siguiente ejemplo sirve de esquema general:

class EstadoJuego: public Estado, public OIS::KeyListener {
    public:
        ...
        EstadoJuego();
        ...
    private:
        ...
        // Se dispara cuando se pulsa una tecla
        virtual bool keyPressed(const OIS::KeyEvent &arg);
 
        // Se dispara cuando se libera una tecla
        virtual bool keyReleased(const OIS::KeyEvent &arg);
        ...
};

Los manejadores de eventos de teclado reciben un objeto "KeyEvent" como argumento para obtener más información sobre el mismo. La estructura "KeyEvent" se compone de dos atributos públicos:

  • const KeyCode key: valor del enumerado "KeyCode" explicado en apartados anteriores correspondiente a la tecla en cuestión.
  • unsigned int text: valor textual de la tecla implicada en el evento.

Bien, nuestra clase "EstadoJuego" está preparada para recibir eventos de teclado pero para ello, debemos registrarla como "Listener". Es decir, hay que decirle al manejador de teclado que "estadoJuego" debe ser notificado ante cualquier cambio de su estado. Esta tarea podemos llevarla a cabo a través del método:

virtual void OIS::KeyBoard::setEventCallback(KeyListener *keyListener);

Imaginad que tenemos una clase que controla todos los estados de juego y sus transicones llamada "EstadosControl". Es la encargada de inicializar el motor del juego y OIS entre otros. Podría tener un esquema similar al siguiente:

class EstadosControl {
    public:
        ...
        EstadosControl();
        ...
    private:
        ...
        // Gestión de la entrada con OIS
        OIS::InputManager* mGestorEntrada;
        OIS::Keyboard* mTeclado;
        OIS::Mouse* mRaton;
        OIS::JoyStick* mJoystick;
        ...
        // Estados de juego
        ...
        Estado* estadoJuego
        ...
};

Cuando cree un objeto de tipo "EstadoJuego" debe registrarlo como observador de eventos de teclado. Lo hacemos de la siguiente manera:

// Es el momento de construir un objeto de la clase EstadoJuego:
estadoJuego = new EstadoJuego();
// Lo registramos como observador de eventos del teclado
mTeclado->setEventCallback(estadoJuego);

A partir de este momento, cada vez que se produzca un evento se dispararán los manejadores de "EstadoJuego". No se puede olvidar de actualizar el estado de "mTeclado" en cada iteración del bucle de juego.

OIS::MouseListener

"OIS::MouseListener" es el observador de eventos del ratón en OIS. Vamos a seguir un esquema de ejemplos idéntico al empleado en "OIS::KeyListener". Tenemos una clase "EstadoJuego" que hereda de la interfaz "OIS::MouseListener" e implementa los métodos correspondientes para la captura de eventos de ratón.

class EstadoJuego: public Estado, public OIS::MouseListener {
    public:
        ...
        EstadoJuego();
        ...
    private:
        ...
        // Se dispara cuando se mueve el ratón
        virtual bool mouseMoved(const OIS::MouseEvent &arg);
 
        // Se dispara cuando se pulsa un botón del ratón
        virtual bool mousePressed(const OIS::MouseEvent &arg, OIS::MouseButtonID id);
 
        // Se dispara cuando se suelta un botón del ratón
        virtual bool mouseReleased(const OIS::MouseEvent &arg, OIS::MouseButtonID id);
        ...
};

Los manejadores de eventos de ratón pueden recibir un objeto del tipo "OIS::MouseEvent". En el caso de que haya algún botón implicado en el evento se adjunta el valor del enumerado "OIS::MouseButtonID" correspondiente (ya explicado en apartados anteriores). La clase "OIS::MouseEvent" cuenta con un sólo atributo público:

  • const MouseState& state: objeto de la clase "OIS::MouseState" (ya comentada) con la información completa del estado interno del ratón.

Al igual que con "OIS:KeyListener", tenemos que registrar nuestro objeto de la clase "EstadoJuego" como observador de eventos de ratón. Seguimos con nuestro ejemplo anterior, en este caso debemos usar el método:

virtual void OIS::Mouse::setEventCallback(MouseListener *mouseListener);

Lo hacemos de la siguiente manera:

// Es el momento de construir un objeto de la clase EstadoJuego:
estadoJuego = new EstadoJuego();
// Lo registramos como observador de eventos del ratón
mRaton->setEventCallback(estadoJuego);

Por supuesto, en cada iteración del bucle de juego no debemos olvidarnos de actualizar el estado interno de "mRaton", de lo contrario, no recibiremos nuevos eventos.

OIS::JoyStickListener

"OIS::JoyStickListener" es el observador de eventos de palancas de juego en OIS. En esta ocasión también continuaremos en la misma línea de los ejemplos anteriores con el fin de mantener un estilo uniforme. De nuevo, tenemos una clase "EstadoJuego" que hereda de la interfaz "OIS::JoyStickListener" e implementa los métodos correspondientes para la captura de eventos de pads o palancas de juego.

class EstadoJuego: public Estado, public OIS::JoyStickListener {
    public:
        ...
        EstadoJuego();
        ...
    private:
        virtual bool buttonPressed (const JoyStickEvent &arg, int button);
        virtual bool buttonReleased (const JoyStickEvent &arg, int button);
        virtual bool axisMoved (const JoyStickEvent &arg, int axis);
        virtual bool sliderMoved (const JoyStickEvent &, int index);
        virtual bool povMoved (const JoyStickEvent &arg, int index);
        virtual bool vector3Moved (const JoyStickEvent &arg, int index);
};

En esta ocasión sólo debemos implementar los 3 primeros ya que son los únicos métodos virtuales puros. Todos reciben como parámetro un objeto de la clase "OIS::JoyStickEvent" la cual consta de un único atributo público:

  • const JoyStickState& state: es algo muy parecido a lo que ocurre con "OIS::MouseEvent", tiene el estado completo del Joystick, el cual ha sido explicado previamente.

Los demás parámetros corresponden al índice del botón o el eje involucrados en el evento.

Siguiendo con la tónica llevada hasta el momento debemos registrar nuestro observador de eventos de joystick para que reciba las notificaciones. En esta ocasión utilizamos el método:

virtual void OIS::JoyStick::setEventCallback(JoyStickListener *joyStickListener);

Un sencillo ejemplo:

// Es el momento de construir un objeto de la clase EstadoJuego:
estadoJuego = new EstadoJuego();
// Lo registramos como observador de eventos del joystick
mJoystick->setEventCallback(estadoJuego);

Como es costumbre, en cada iteración del "game loop" será necesario actualizar el estado interno de "mJoystick" si queremos que se disparen los eventos. Ni que decir tiene que OIS debe estar correctamente inicializada y los manejadores de dispositivos creados como se explica en los primeros apartados de este artículo.


Ejemplo

Ejemplo de OIS
Pequeña aplicación que muestra la captura de eventos utilizando OIS
Diagrama de clases del ejemplo OIS

En el siguiente ejemplo se muestra cómo capturar eventos en Ogre utilizando OIS como biblioteca resumiendo las técnicas desglosadas a lo largo de todo el artículo. Tendremos una clase AplicacionOgre que se encargará de iniciar tanto Ogre como OIS y se mantendrá a la escucha de eventos (ratón, teclado y ventana). A continuación el diagrama de clases del ejemplo:

Recuerda que debes preparar los plugins (pues no vienen incluidos en el paquete). Puedes encontrar más información sobre esto en "Creación de un entorno de trabajo multiplataforma". Los plugins necesarios son:

  • RenderSystem_GL

Conclusiones

Tras leer y comprender este artículo serás capaz de controlar la entrada de tu usuario utilizando Object Oriented Input System de forma perfectamente integrada con Ogre. Además conocerás varias aproximaciones a la captura de eventos (Listeners y objetos que representan un dispositivo). Esta biblioteca nos será de gran ayuda a lo largo del resto de artículos de la wiki.

Herramientas personales