miércoles, 3 de julio de 2013

Entrada por Eventos con SDL

En este tutorial vamos a aprender a capturar algunos eventos de teclado y ratón. Os dejo una página del sitio oficial para que veáis todos los eventos que queráis capturar. Aun así habrá otro tutorial con Eventos Avanzado.

Al igual que en el tutorial anterior, tenemos dividido el código fundamentalmente en la cabecera (.h) y la implementacion (.cpp). Con respecto al anterior tutorial, la cabecera prácticamente no ha cambiado, donde antes ponía Demo_1, ahora pone Demo_2, a si que os la dejo aquí para copiar pero doy por hecha la explicación.
#ifndef DEMO_2_H_
#define DEMO_2_H_
#include "SDL2/SDL.h"

class Demo_2 {
private:
    bool running;
    SDL_Window* mainwindow;
    static const uint32_t WIN_HEIGHT = 512; //px
    static const uint32_t WIN_WIDTH  = 512; //px
public:

    Demo_2();
    int Execute(){ return OnExecute(); }
    bool Init(){ return OnInit(); }
    void Loop(){ return OnLoop(); }
    void Render(){ return OnRender(); }
    void Cleanup(){ return OnCleanup(); }
    void Event(SDL_Event* Event){ OnEvent(Event); }



    int OnExecute();

    bool OnInit();
    void OnEvent(SDL_Event* Event);
    void OnLoop();
    void OnRender();
    void OnCleanup();

};


#endif
Con respecto a la implementación si se han producido un par de cambios, pongo el código y lo comentamos.
#include "Demo_2.h"
#include <iostream>

Demo_2::Demo_2() : running(false), mainwindow(NULL) {}

void Demo_2::OnEvent(SDL_Event* event) {
    switch (event->type) {
        case SDL_MOUSEBUTTONUP:
            std::cout << "MOUSE_BUTTON_UP: x:" << event->button.x << "| y:"
                    << event->button.y
                    << "| button1:" << (event->button.button == SDL_BUTTON_LEFT)
                    << "| button2:" << (event->button.button == SDL_BUTTON_RIGHT)
                    << "| button3:" << (event->button.button == SDL_BUTTON_MIDDLE)
                    << "| clickState:" << event->motion.state
                    << std::endl << std::endl;
            break;
        case SDL_MOUSEBUTTONDOWN:
            std::cout << "MOUSE_BUTTON_DOWN: x:" << event->button.x
                    << "| y:" << event->button.y
                    << "| button1:" << (event->button.button == SDL_BUTTON_LEFT)
                    << "| button2:" << (event->button.button == SDL_BUTTON_RIGHT)
                    << "| button3:" << (event->button.button == SDL_BUTTON_MIDDLE)
                    << "| clickState:" << event->motion.state
                    << std::endl << std::endl;
            break;
        case SDL_MOUSEMOTION:
            std::cout << "MOUSE_MOTION: x:" << event->motion.x
                    << "| y:" << event->motion.y
                    << "| xrel:" << event->motion.xrel
                    << "| yrel:" << event->motion.yrel
                    << "| clickState:" << event->motion.state
                    << "| button1:" << ((event->motion.state  & 1) == 1)
                    << "| button2:" << ((event->motion.state  & 4) == 4)
                    << "| button3:" << ((event->motion.state  & 2) == 2)
                    << std::endl << std::endl;
            break;
        case SDL_KEYUP:
            running = event->key.keysym.sym != SDLK_ESCAPE;
            break;
        case SDL_QUIT:
            running = false;
            break;
        default:
            break;
    }
}
void Demo_2::OnLoop() {}
void Demo_2::OnRender() {}

void Demo_2::OnCleanup() {
    SDL_DestroyWindow(mainwindow);
    SDL_Quit();
}

bool Demo_2::OnInit() {
    if(SDL_Init(SDL_INIT_EVERYTHING) < 0)
        return false;

    mainwindow = SDL_CreateWindow("Titulo de la Ventana",
            SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
            WIN_WIDTH, WIN_HEIGHT,
            SDL_WINDOW_SHOWN);
    if(!mainwindow)
        return false;

    running = true;

    return true;
}

int Demo_2::OnExecute() {
    if (!Init())
        return -1;

    SDL_Event event;

    while (running) {

        while (SDL_PollEvent(&event))
            Event(&event);

        Loop();
        Render();
    }

    Cleanup();

    return 0;
}
He quitado todas las trazas para dejar la salida limpia para lo que nos concierne, el Input, que ahora tiene las trazas pertinentes.
Los principales cambios se han producido en OnExecute y en OnEvent.

  • OnExecute: Hemos quitado el contador de 10 vueltas y ahora tenemos un objeto del tipo SDL_Event para capturar los eventos.
    Después tenemos igual que antes el bucle en el que tenemos la función Loop y Render y al principio hemos añadido otro bucle. Este bucle se encarga de sacar todos los eventos (SDL_PollEvent) que se han producido en esa vuelta de bucle y los envía a la función Event para tratar. Si os fijáis, lo que hacemos es pasarle el puntero al evento, por eso el PollEvent puede guardar en tu estructura el evento.
    Por lo demás el OnExecute no tiene mucho más que comentar.
  • OnEvent: Recibimos un evento, ¿Que forma tiene un evento?
    Según la página oficial esta es su representación de evento.
    typedef union{
      Uint8 type;
      SDL_ActiveEvent active;
      SDL_KeyboardEvent key;
      SDL_MouseMotionEvent motion;
      SDL_MouseButtonEvent button;
      SDL_JoyAxisEvent jaxis;
      SDL_JoyBallEvent jball;
      SDL_JoyHatEvent jhat;
      SDL_JoyButtonEvent jbutton;
      SDL_ResizeEvent resize;
      SDL_ExposeEvent expose;
      SDL_QuitEvent quit;
      SDL_UserEvent user;
      SDL_SywWMEvent syswm;
    } SDL_Event;
    Y si ahora vemos otro evento cualquiera vemos que es de la forma:
    typedef struct{
      Uint8 type;
      Uint8 state;
      SDL_keysym keysym;
    } SDL_KeyboardEvent;
    Todos empiezan con Uint8 type; y después tienen mas información. Si os fijáis y sabéis lo que son, la estructura del Evento es una Union y no una struct, por eso todas las implementaciones de evento deben tener el type al principio. 
    Para los que no sepan lo que es, una Union hace que todo lo que este dentro de las llaves ocupe la misma posición de memoria, dependiendo del tipo de evento se rellenan mas o menos bits. Esto que han hecho es una especie de simulación de herencia o interfaz para struct. La explicación se ha quedado un poco pobre, si no te ha quedado claro mira en Internet que encontraras mucha información.
    Por ello lo primero que hace la función OnEvent es mirar que tipo de evento estamos tratando, para ello hacemos un switch con el type del evento.
    Hemos capturado 5 tipos como ejemplo, aquí podéis encontrar todos (Input Events):
    http://wiki.libsdl.org/APIByCategory
    Después de capturar el tipo de evento accedemos al tipo de evento correspondiente en la estructura, por ejemplo: 
    event->button Y a la propiedad que deseemos, SDL_MOUSEBUTTONUP al ser un evento de ratón podemos capturar la posicion x e y, el botón del ratón que se ha liberado (lógicamente solo puede ser uno a la vez, por muy rápidos que seamos es casi imposible liberar dos a la vez) y el estado en el que está el ratón.
    En el caso de mousemotion existen xrel e yrel que te dan la posición relativa a la anterior posición del ratón. El caso del estado te da información acerca de que botones están pulsados con una mascara binaria, para poder obtener cuales están pulsados utilizo la & binaria y no la && lógica. He comprobado que obtiene hasta 5 botones del ratón en mi caso. Imagino que no dependerá del ratón, a si que, si quieren mas botones de ratón que se los bindeen a teclas especiales :P.
    Para ejemplificar el teclado he puesto que cuando se presiona la tecla Escape (Esc) se termine el programa. Esa porción de código es suficientemente simple, no me paro más.
    Por ultimo SDL_QUIT corresponde al evento de hacer click en la X de la ventana, también salimos del programa. Si no lo configuramos, no podremos salir dándole a la X.
Para probar el ejemplo igual que en el anterior, crearos un main con un objeto del tipo Demo_2 y llamad a Execute(). Deberiais tener una ventana y una traza en la consola de los eventos que hemos capturado, el mouse motion y el mouse click no se capturan si no estas encima de la ventana.
Aquí se termina este tutorial, espero os haya sido útil. Os dejo un enlace a la documentación de la API, en el apartado Input Events aparecen todos los eventos: http://wiki.libsdl.org/APIByCategory

No hay comentarios:

Publicar un comentario