Nociones Básicas Sobre OGRE Parte 3A
Agosto 22, 2008
3A. Entrada por teclado usando OIS. Unbuffered.
Lo prometido es deuda, veremos ahora como realizar la entrada por medio de teclado y ratón usando la librería OIS. He decidido separar esta entrega en dos partes, entrada Unbuffered y entrada Buffered, esto para que no sea una lección muy larga, y porque también la forma en que se implementa una y otra es un poco diferente.
En la entrada Unbuffered se revisa constantemente el estado del teclado o ratón para verificar si hemos pulsado o no una tecla o botón. Si llegáramos a realizar alguna acción mientras no estamos revisando el teclado o ratón, ésta acción se pierde ya que no se guarda. Esto por lo general no afecta ya que la lectura se realiza varias veces por segundo, tantas como cuadros (Frame) realice nuestra aplicación.
Por el contrario, en la entrada Buffered, los eventos se reciben por medio de un mecanismo de llamadas (callback). Esto significa que cada vez que pulsamos una tecla o un botón del ratón, este evento se guarda en un área de memoria llamada buffer de eventos, de donde posteriormente son enviados a nuestra aplicación, y de esa forma nunca perdemos ninguna acción que realicemos.
Lo mas común es utilizar entrada Buffered para los menús y GUI’s de nuestra aplicación, y la entrada Unbuffered para todo lo demás, aunque eso también dependerá del tipo de cosas que queramos hacer. Recomiendo que prueben los dos, y decidan cual se adapta mejor a la tarea..
Ya que sabemos esto, ahora si podemos proceder a realizar el código para la captura de eventos por teclado y ratón. Para esto utilizaremos tres clases de OIS, InputManager, Keyboard y Mouse. La clase InputManager nos permite inicializar el sistema de entrada, diciéndole sobre que ventana queremos hacer la captura de eventos, para esto, necesitamos pasarle un “HANDLE” a la ventana, este handle lo vamos a obtener por medio de RenderWindow::getCustomAttribute(). Para que OIS acepte este handle, necesitamos convertirlo a un formato llamado OIS::ParamList. Es un poco difícil de explicar, así que mejor pongo a continuación el código completo que necesitamos para crear el InputManager.
size_t windowHnd;
OIS::ParamList pl;
// Obtenemos el handle a la ventana de render
mWindow->getCustomAttribute("WINDOW", &windowHnd);
// Convertimos el handle en un ParamList
std::ostringstream windowHndStr;
windowHndStr << windowHnd;
pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));
// Creamos nuestro Input Manager
mInputManager = OIS::InputManager::createInputSystem(pl);
Una vez creado nuestro InputManager, lo utilizaremos para crear los objetos mKeyboard y mMouse, que nos permitirán manejar el teclado y ratón respectivamente. El primer parametro representa el tipo de dispositivo, y el segundo parametro sera true para modo buffered, y false para unbuffered. Debemos hacer ademas una conversion (cast) para el tipo de entrada:
mKeyboard = static_cast<ois::keyboard *> (mInputManager->createInputObject(OIS::OISKeyboard, this->bufferedInput));
mMouse = static_cast<ois::mouse *> (mInputManager->createInputObject(OIS::OISMouse, this->bufferedInput));
Listo! con esto ya hemos creado los objetos necesarios para capturar la entrada. Ahora, cada vez que necesitemos hacer la captura, debemos de llamar al método capture de los objetos Keyboard y Mouse, este codigo lo podemos poner en nuestro método FrameStarted():
mKeyboard->capture();
mMouse->capture();
Ahora, para saber que si pulsamos una tecla y cual fue, llamamos al método mKeyboard->isKeyDown(), en este ejemplo, vamos a comprobar si la tecla pulsada es ESC con el siguiente código:
if (mKeyboard->isKeyDown(OIS::KC_ESCAPE) == true)
Los códigos de teclado comienzan con KC_, para las teclas de cursor, su códigos serían:
- Izquierda = KC_LEFT
- Derecha = KC_RIGHT
- Arriba = KC_UP
- Abajo= KC_DOWN;
La mayoría de códigos son auto explicativos, Si quieres saber el resto de códigos de teclado, puedes verlos en el archivo OISKeyboard.h.
Para saber el estado del ratón, utilizamos el método mMouse->getMouseState(), que nos devuelve información sobre que botón del ratón hemos pulsado, y en que posición de la pantalla nos encontramos. En este caso queremos saber si hemos pulsado el botón izquierdo del ratón, podemos usar esta información directamente:
if (mMouse->getMouseState().buttonDown(OIS::MB_Left))
O podemos usar una variable intermedia para leer la información:
OIS::MouseState ms = mMouse->getMouseState();
if (ms.buttonDown(OIS::MB_Left))
Finalmente, antes de salir de nuestra aplicación, debemos destruir todo el sistema de entrada que hemos creado, esto lo hacemos con el siguiente código:
mInputManager->destroyInputObject(mKeyboard);
mInputManager->destroyInputObject(mMouse);
OIS::InputManager::destroyInputSystem(mInputManager);
Ahora, con esta información, vamos a modificar el código de la lección pasada para que podamos salir de nuestra aplicación cuando pulsemos ESC o el botón izquierdo del ratón. Añadiremos 4 variables a nuestra clase, mInputManager, mKeyboard, mMouse, y bufferedInput, esta última para que guardemos la elección de si queremos utilizar el modo buffered o el unbuffered.
También crearemos 5 métodos, primero initInputSystem(), en donde pondremos la inicialización del InputManager, luego initAllInputs(), que llamara a 2 métodos más initKeyboardInputs() e initMouseInputs(), que es donde crearemos los objetos que se encargaran del manejo de la entrada, captureAllDevices(), en donde llamaremos a los métodos capture de los objetos mkeyboard y mMoiuse, eliminaremos el código en FrameStarted() que cerraba la aplicación pasados 3 segundos y agregaremos ahora la condición para salir del programa con ESC o botón izquierdo del ratón, y también agregaremos el código para destruir el sistema de entrada al finalizar la aplicación. A continuación les dejo el código completo con comentarios:
OgreTutorial3.H
#include "Ogre.h"
#include "OIS\OIS.h"
class OgreApp : Ogre::FrameListener
{
public:
Ogre::Root* mRoot;
Ogre::RenderWindow* mWindow;
Ogre::SceneManager* mSceneMgr;
Ogre::Camera* mCamera;
Ogre::Viewport* mViewport;
OIS::InputManager* mInputManager;
OIS::Keyboard* mKeyboard;
OIS::Mouse* mMouse;
bool exit;
bool bufferedInput;
OgreApp()
{
exit = false;
mRoot = NULL;
mWindow = NULL;
mInputManager = NULL;
}
~OgreApp()
{
if (mWindow != NULL)
{
if (mInputManager != NULL)
{
mInputManager->destroyInputObject(mKeyboard);
mInputManager->destroyInputObject(mMouse);
OIS::InputManager::destroyInputSystem(mInputManager);
mInputManager = NULL;
}
}
if (mRoot != NULL)
delete mRoot;
}
void go()
{
createRoot();
setupRenderSystem();
if (exit == true)
return;
else
{
createRenderWindow();
initInputSystem(false);
initAllInputs();
initEventHandlers();
createScene();
startRenderLoop();
}
}
void createRoot()
{
// Crea el objeto root, la base de todo el sistema de OGRE
mRoot = new Ogre::Root();
}
void setupRenderSystem()
{
// Mostrar cuadro de dialogo para elegir sistema de render
// y resolucion de la ventana
if (!mRoot->showConfigDialog())
{
exit = true;
}
}
void createRenderWindow()
{
// Crea una ventana por defecto, con un titulo especificado
mWindow = mRoot->initialise(true, "Mi Aplicacion con OGRE");
}
void initInputSystem(bool bufferedMode)
{
size_t windowHnd;
OIS::ParamList pl;
Ogre::LogManager::getSingleton().logMessage("*** Iniciando OIS ***");
bufferedInput = bufferedMode;
// Obtenemos el handle a la ventana de render
mWindow->getCustomAttribute("WINDOW", &windowHnd);
// Convertimos el handle en un ParamList
std::ostringstream windowHndStr;
windowHndStr << windowHnd;
pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));
// Creamos nuestro Input Manager
mInputManager = OIS::InputManager::createInputSystem(pl);
}
void initAllInputs()
{
initKeyboardInputs();
initMouseInputs();
}
void initEventHandlers()
{
mRoot->addFrameListener(this);
}
void createScene()
{
// Elegir manejador de escenario
mSceneMgr = mRoot->createSceneManager(Ogre::ST_GENERIC);
// Crear camara y posicionarla en el espacio
mCamera = mSceneMgr->createCamera("CamaraPrincipal");
mCamera->setPosition(Ogre::Vector3(0.0f, 0.0f, 500.0f));
mCamera->lookAt(Ogre::Vector3(0.0f, 0.0f, 0.0f));
mCamera->setNearClipDistance(5.0f);
mCamera->setFarClipDistance(5000.0f);
// Agregar camara al viewport
mViewport = mWindow->addViewport(mCamera);
mViewport->setBackgroundColour(Ogre::ColourValue(0.0f, 0.0f, 0.0f));
}
void startRenderLoop()
{
// Comenzar el despliegue
mRoot->startRendering();
}
bool frameStarted(const Ogre::FrameEvent &evt)
{
// Capturamos la entrada
captureAllDevices();
// Comprobamos si pulsamos la tecla ESC
if (mKeyboard->isKeyDown(OIS::KC_ESCAPE) == true)
exit = true;
// Comprobamos si pulsamos el boton izquierdo del raton
OIS::MouseState ms = mMouse->getMouseState();
if (ms.buttonDown(OIS::MB_Left))
exit = true;
if (exit == true)
return false;
else
return true;
}
bool frameEnded(const Ogre::FrameEvent &evt)
{
return true;
}
void initKeyboardInputs()
{
Ogre::LogManager::getSingleton().logMessage("*** Iniciando entrada por teclado (Keyboard) ***");
mKeyboard = static_cast<OIS::Keyboard*> (mInputManager->createInputObject(OIS::OISKeyboard, this->bufferedInput));
}
void initMouseInputs()
{
Ogre::LogManager::getSingleton().logMessage("*** Iniciando entrada por raton (Mouse) ***");
mMouse = static_cast<OIS::Mouse*> (mInputManager->createInputObject(OIS::OISMouse, this->bufferedInput));
}
void captureAllDevices()
{
if (mKeyboard != NULL)
{
mKeyboard->capture();
}
if (mMouse != NULL)
{
mMouse->capture();
}
}
};
OgreTutorial3.cpp
#include "OgreTutorial3.h"
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
#else
int main()
#endif
{
OgreApp* app = new OgreApp();
app->go();
delete app;
return 0;
}
En la siguiente entrega veremos como implementar el sistema de entrada Buffered. Por el momento es todo. Espero sus comentarios.
Actualización 13/Oct/2008: Modificado OIS::MouseButtonID::MB_Left por OIS::MB_Left para evitar un error de compilación en Linux.
Entry Filed under: OGRE. .
5 Comments Add your own
Leave a Comment
Some HTML allowed:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <pre> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>
Trackback this post | Subscribe to the comments via RSS Feed
1.
ibitox | Agosto 25, 2008 at 2:33 pm
Muy buen tutorial ProfesorX
2.
dr_babosa | Octubre 13, 2008 at 10:59 am
gracias amigo, excelentes tutoriales. Me vienen funcionando a la perfección, a excepción de un error que me da este, “error: ‘OIS::MouseButtonID’ is not a class or namespace”. Puede ser que tenga una versión diferente de OIS? (la instale desde los repositorios de ubuntu). La verdad es que estoy empezando con c++ y todavía no entiendo bien los errores del compilador (llegar a este punto fue una odisea). Mil gracias por tu ayuda, espero ansioso mas tutoriales!
3.
profesorx | Octubre 13, 2008 at 5:47 pm
Me parece que deberas cambiar OIS::MouseButtonID:MB_Left por OIS::MB_Left, no uso Linux, pero mi compilador me daba un warning en esa linea, pero como solo era un warning, deje el codigo tal cual, ya he cambiado esa linea, y no me da warnings, si eso resuelve tu problema, házmelo saber
4.
dr_babosa | Octubre 15, 2008 at 4:42 pm
Genial, justo acababa de resolver el problema. Tal cual… la solución fue cambiar OIS::MouseButtonID:MB_Left por OIS::MB_Left. Gracias por la mano! estoy empezando con ogre y con c++ al mismo tiempo y se me pone complicado, gracias por compartir tus conocimientos.
5.
vaxter | Octubre 29, 2008 at 6:32 am
Te agradezco mucho el esfuerzo de estos 3 tutoriales.
Me han servido de ayuda recien introducido al orgre para comprender como funcionan las clases que trae y saber buscar en la Api.
Espero ansioso una dosis mas de estos tutoriales, y nuevamente Gracias, si querías ayudar por lo menos a mi lo has conseguido.
Una sugerencia pon en blog un form de contactame
Un saludo