Problema detectando clicks

Tratamos sobre el manejo de APIs frecuentemente utilizadas en el desarrollo de videojuegos, como SDL, pygame o SFML.

Problema detectando clicks

Notapor RafaG » Jue Sep 30, 2010 7:32 pm

Hola, tengo otro problema.

Estoy haciendo un juego en el que utilizo dos clases, una para los escenarios y otra para los elementos con los que se interactúa.
La clase ESCENARIO tiene una función miembro que detecta si se ha hecho clic a un elemento, el problema es que no se detectan los clics, creo que es porque al crear dos elementos y llamar a la función ocurre algún problema. Esta es la función:

Código: Seleccionar todo
int ESCENARIO::Click(ITEM ELEMENTO)
{
    if (ELEMENTO.Mostrar==1)
    {
        if (SDL_PollEvent(&evento))
        {
            if (evento.type==SDL_MOUSEBUTTONDOWN)
            {
                if (evento.button.x>ELEMENTO.pos.x && evento.button.x<ELEMENTO.pos.x+ELEMENTO.img->w && evento.button.y>ELEMENTO.pos.y && evento.button.y<ELEMENTO.pos.y+ELEMENTO.img->h)
                {
                    return 1;
                }
                else
                {
                    return 0;
                }
            }
        }
    }
    return 0;
}


El problema solo ocurre cuando se crean dos elementos, por ejemplo LIBRO y RELOJ y se comprueba si se hace clic sobre ellos, porque se llama dos veces a la función SDL_PollEvent(...).

¿Cómo lo puedo solucionar?

Saludos y gracias.
RafaG
 
Mensajes: 20
Registrado: Lun Abr 05, 2010 10:57 am

Re: Problema detectando clicks

Notapor Juanxo » Jue Sep 30, 2010 9:31 pm

jajajaja esto tiene delito tío... como lo vea Hugo... xD

En el motor de Pilas de Losersjuegos YA hay implementada una manera de hacer eso!! xD Creo que son las Habilidades y la clase Estudiante o algo así.
En el de C++ por ejemplo, lo tenemos de esta manera.

Para cada evento que queremos registrar, creamos una función que se encargue de manejar el comportamiento (en tu caso sería la función click). Básicamente lo que tenemos es un gestor de este tipo de funciones. Para cada uno tenemos una tupla que asocia el nombre del evento con la función a ejecutar cuando ocurre y una lista con todos los objetos que están interesados en ese evento (esto suele ser un patrón Listener/Observador). Cada vez que ocurre un evento, se lo comentamos al gestor de eventos, y si tiene alguna tupla de las que te he comentado antes para ese evento, pues ejecuta la función en todos los elementos que tiene asociado.

Creo que será más sencillo si te miras los códigos de Pilas y PilasCPP (creo que los tienes en los archivos de código habilidades o actores)
Avatar de Usuario
Juanxo
 
Mensajes: 437
Registrado: Sab Ene 31, 2009 2:34 am
Ubicación: Madrid(España)

Re: Problema detectando clicks

Notapor carlostex » Vie Oct 01, 2010 2:01 am

Si lo haces como lo estas haciendo hasta ahora y no te metes con las señales, que resulta mas elegante y sencillo cuando se usa, pero la implementación es un poco mas compleja, Me faltaría ver que mas haces con el valor devuelto, debes llamar a la función por cada ítem, otra cosa, ese código tiene un serio problema, sabes que los eventos se manejan como una cola, cuando tu haces poolevent se saca de la cola es evento, y solo verificas si ese evento es del mouse, si no lo fuera lo desechas y no lo procesaste y hay esta el problema, la solución seria hacer una función que saque de la cola un evento, y verificas que tipo es y sus parámetros, si es uno que te interesa en este caso el botón presionado envías los parámetros a este caso llamas a la función por cada item que tengas y le pasas lo que te devuelve el evento que es la posición del mouse, de esa forma procesas todos lod eventos, claro te va a quedar una función grande y una sopa de codigo, con las señales y lo que dice Juan es súper fácil.
Bajate el código de pilascpp y checate los archivos de habilidades, haí esta la función implementada solo copialo y pega.
http://bitbucket.org/hugoruscitti/pilascpp
El conocimiento de unos es conocimiento de todos.
Avatar de Usuario
carlostex
 
Mensajes: 249
Registrado: Mar Jul 14, 2009 4:13 am
Ubicación: mexico

Re: Problema detectando clicks

Notapor hugoruscitti » Vie Oct 01, 2010 2:43 pm

RafaG escribió:El problema solo ocurre cuando se crean dos elementos, por ejemplo LIBRO y RELOJ y se comprueba si se hace clic sobre ellos, porque se llama dos veces a la función SDL_PollEvent(...).

¿Cómo lo puedo solucionar?


Si, creo que justamente uno de los problemas es cuando se llama a SDL_PollEvent y éste
limpia la cola de eventos. Así que una forma de solucionarlo puede ser llamar
a SDL_PollEvent una sola ver por iteración del bucle.

Por ejemplo, imagina que tu bucle de juego es algo así:

Código: Seleccionar todo
while (1)
{
     escenario->click(libro)
     escenario->click(reloj)
     [...]
}


entonces, un enfoque distinto podría ser llamar a SDL_PollEvent una
sola vez, y enviarle el evento a todos los componentes:

Código: Seleccionar todo
while (1)
{
      if (SDL_PollEvent(&evento))
      {
             escenario->atender_evento(evento)
      }
      [...]
}


Es decir, tendrías que "sacar" la llamada a la función SDL_PollEvent
afuera del escenario.

Luego, cada objeto ("libro", "reloj" etc..) tendría que
implementar el método "atender_evento" que el bucle le dá:


Código: Seleccionar todo
void escenario::antender_evento(Evento * event)
{
      libro.anteder_evento(event);
      reloj.atender_evento(event);
}


Así, la lógica es un poco mas sencilla. Porque cada libro o reloj va
a hacer lo siguiente: recibe el evento y se fija si le interesa, por
ejemplo, si es un click se fija si está dentro de su area y si
lo está hace algo.

¿se ajusta a lo que estás buscando?, ¿en realidad el escenario tiene dentro
referencias los libros y relojes no?
Avatar de Usuario
hugoruscitti
Site Admin
 
Mensajes: 1242
Registrado: Dom Jul 30, 2006 3:57 am
Ubicación: Buenos Aires, Argentina

Re: Problema detectando clicks

Notapor RafaG » Vie Oct 01, 2010 4:40 pm

No se ajusta complétamente, habrá varios escenarios, cada escenario tendrá sus objetos, si la función del escenario detectase los clics de objetos de escenarios anteriores se estaría perdiendo tiempo, además que quiero que, si en un futuro edito el código, quiero que casi todo lo que tenga que editar esté en la función main() para hacer la tarea más simple, pero si hago lo del ejemplo también habría que añadir cosas a la clase (¿se me entiende?). Pero si no se puede, no quedará otra que adaptarse a las limitaciones.

Al principio la función Click iba a estar en la clase ITEM, no en ESCENARIO, pero me di cuenta que la clase ESCENARIO debía contener el número de elementos que había que encontrar y que desde ITEM no se podía acceder a ESCENARIO. Bueno, sí se puede, pero habría que especificar al escenario al que habría que acceder y como he dicho habrá varios escenarios.

Dejo aquí el código completo:
Código: Seleccionar todo
#include <iostream.h>
#include <SDL.h>

#define DETECTIVE 256
#define AYUDANTE 257
void Iniciar();
void ErrorSDL();
void Salir();

SDL_Surface *pantalla,*detective,*ayudante,*fondotxt;
SDL_Event evento;
SDL_Rect PosCara, PosFondoTXT,PosTexto;
Uint8 *tecla;

class ITEM
{
    public:
    SDL_Surface*img;
    SDL_Rect pos;
    int Mostrar;
    int NivelDesvanecimiento;
    int Clicado;

    void Conversacion(int PERSONAJE,SDL_Surface*txt);
    void Imprimir();
    void Desvanecer();
};
class ESCENARIO
{
    public:
    int NUMERO_OBJETOS;
    int OBJETOS_ENCONTRADOS;

    SDL_Surface*img;
    SDL_Rect pos;
    int Click(ITEM ELEMENTO);
    void Imprimir();

};

void ESCENARIO::Imprimir()
{
    SDL_BlitSurface(img,NULL,pantalla,&pos);
}

int ESCENARIO::Click(ITEM ELEMENTO)
{
    if (ELEMENTO.Mostrar==1)
    {
        if (SDL_PollEvent(&evento))
        {
            if (evento.type==SDL_MOUSEBUTTONDOWN)
            {
                if (evento.button.x>ELEMENTO.pos.x && evento.button.x<ELEMENTO.pos.x+ELEMENTO.img->w && evento.button.y>ELEMENTO.pos.y && evento.button.y<ELEMENTO.pos.y+ELEMENTO.img->h)
                {
                    return 1;
                }
                else
                {
                    return 0;
                }
            }
            if (evento.type==SDL_KEYDOWN)
            {
                return 0;
            }
        }
    }
    return 0;
}


void ITEM::Imprimir()
{
    if (Mostrar==1)
    {
        SDL_BlitSurface(img,NULL,pantalla,&pos);
    }
}

void ITEM::Conversacion(int PERSONAJE,SDL_Surface*txt)
{
    while (1)
    {
        SDL_BlitSurface(fondotxt,NULL,pantalla,&PosFondoTXT);
        if (PERSONAJE==DETECTIVE)
        {
            SDL_BlitSurface(detective,NULL,pantalla,&PosCara);
        }
        if (PERSONAJE==AYUDANTE)
        {
            SDL_BlitSurface(ayudante,NULL,pantalla,&PosCara);
        }
        //SDL_BlitSurface(txt,NULL,pantalla,&PosTexto);
        SDL_Flip(pantalla);
        if (SDL_PollEvent(&evento))
        {
            if (evento.type==SDL_MOUSEBUTTONDOWN)
            {
                break;
            }
        }
    }

}

void ITEM::Desvanecer()
{
    if (NivelDesvanecimiento>0)
    {
        SDL_SetAlpha(img,SDL_SRCALPHA|SDL_RLEACCEL,NivelDesvanecimiento);
        NivelDesvanecimiento-=1;
    }


}


ESCENARIO CASA_ADELA;
ITEM RELOJ,LIBRO;


int main ( int argc, char** argv )
{
    Iniciar();
    while (1)
    {
        CASA_ADELA.Imprimir();
       
        if (CASA_ADELA.Click(RELOJ)==1 || RELOJ.Clicado==1)
        {
            if (RELOJ.Clicado==1)
            {
                RELOJ.Desvanecer();
                if (RELOJ.NivelDesvanecimiento<=0)
                {
                    RELOJ.Mostrar=0;
                    RELOJ.Clicado=0;
                    RELOJ.NivelDesvanecimiento=255;
                    RELOJ.Conversacion(DETECTIVE,0);
                    RELOJ.Conversacion(AYUDANTE,0);
                }
            }
            else
            {
                RELOJ.Clicado=1;
            }
        }
        if (CASA_ADELA.Click(LIBRO)==1 || LIBRO.Clicado==1)
        {
            if (LIBRO.Clicado==1)
            {
                LIBRO.Desvanecer();
                if (LIBRO.NivelDesvanecimiento<=0)
                {
                    LIBRO.Mostrar=0;
                    LIBRO.Clicado=0;
                    LIBRO.NivelDesvanecimiento=255;
                    LIBRO.Conversacion(DETECTIVE,0);
                    LIBRO.Conversacion(AYUDANTE,0);
                }
            }
            else
            {
                LIBRO.Clicado=1;
            }
        }
       
        RELOJ.Imprimir();
        LIBRO.Imprimir();
        SDL_Flip(pantalla);

        SDL_PumpEvents();
        tecla=SDL_GetKeyState(NULL);
        if (tecla[SDLK_ESCAPE])
        {
            Salir();
        }


    }
    return 0;
}


void Iniciar()
{
    if (SDL_Init(SDL_INIT_VIDEO)<0)
    {
        ErrorSDL();
    }

    pantalla=SDL_SetVideoMode(700,500,24,SDL_HWSURFACE|SDL_DOUBLEBUF);
    if (pantalla==NULL)
    {
        ErrorSDL();
    }

    CASA_ADELA.img=SDL_LoadBMP("DATA/IMG/escenario.bmp");
    CASA_ADELA.NUMERO_OBJETOS=2;
    CASA_ADELA.OBJETOS_ENCONTRADOS=0;

    RELOJ.img=SDL_LoadBMP("DATA/IMG/reloj.bmp");
    SDL_SetColorKey(RELOJ.img, SDL_SRCCOLORKEY|SDL_RLEACCEL,SDL_MapRGB(RELOJ.img->format, 255, 0, 255));
    RELOJ.pos.x=40;
    RELOJ.pos.y=435;
    RELOJ.Mostrar=1;
    RELOJ.NivelDesvanecimiento=255;

    LIBRO.img=SDL_LoadBMP("DATA/IMG/libro.bmp");
    SDL_SetColorKey(LIBRO.img, SDL_SRCCOLORKEY|SDL_RLEACCEL,SDL_MapRGB(LIBRO.img->format, 255, 0, 255));
    LIBRO.pos.x=478;
    LIBRO.pos.y=80;
    LIBRO.Mostrar=1;
    LIBRO.NivelDesvanecimiento=255;

    detective=SDL_LoadBMP("DATA/IMG/detective.bmp");
    PosCara.x=10;
    PosCara.y=360;

    ayudante=SDL_LoadBMP("DATA/IMG/ayudante.bmp");

    fondotxt=SDL_LoadBMP("DATA/IMG/fndtxt.bmp");
    PosFondoTXT.x=0;
    PosFondoTXT.y=500-160;

}

void ErrorSDL()
{
    cerr << "Error: " << SDL_GetError() << endl;
    Salir();
}

void Salir()
{
    SDL_FreeSurface(pantalla);
    ///[...]
    exit(0);
}
/****************************************************************************************/


Por cierto, ¿qué son la señales?

Saludos.
RafaG
 
Mensajes: 20
Registrado: Lun Abr 05, 2010 10:57 am

Re: Problema detectando clicks

Notapor carlostex » Vie Oct 01, 2010 6:01 pm

El unico limite es lo que estes dispuesto a hacer, se puede hacer que solo en el main modifiques cosas, para esto la solucion que se me ocurre es la que te aviamos dicho, usar señales.
Que son las señales?. lo puedes encontrar aqui http://www.boost.org/doc/libs/1_44_0/doc/html/signals/tutorial.html#id2535377 y veras algunos ejemplos. Revisa el codigo de pilascpp pues ya esta implementado, y es esactamente lo que quieres.

con esto defines una funcion que por ejemplo detecte si has pulsado sobre un objeto, que puede ser la misma que tu nos mostraste, entes de bucle del juego le dices al getor de señales que registre esa funcion, y las que quieras, en el bucle solo tienes que realizar una llamada a una funcion y eso llamara y pasara los argumentos a todas las funciones registradas
Claro que puedes hacer eso dentro de la funcion updtae del juego, si es que la tienes, y eso delinda al usuario a preocuparse por llamar al evento.
El conocimiento de unos es conocimiento de todos.
Avatar de Usuario
carlostex
 
Mensajes: 249
Registrado: Mar Jul 14, 2009 4:13 am
Ubicación: mexico

Re: Problema detectando clicks

Notapor Juanxo » Vie Oct 01, 2010 7:04 pm

Básicamente las señales(a lo mejor te suenan más como callbacks o algo parecido) son una manera de llamar a funciones en respuesta a cualquier tipo de evento, o por el motivo que te de la gana. El tema es que cuando yo emito una señal, todas las funciones conectadas a dicha señal se ejecutarán, por lo que son perfectas para este tipo de sistemas de gestores de eventos.


En pseudocódigo sería algo así (ejemplo que se encarga de limpiar el juego al salir)

Código: Seleccionar todo
while (esteJugando)
{
  while (me queden eventos por procesar)
    gestor_eventos.procesar(evento)
}

clase GestorEventos
{
 
  GestorEventos
  {
     m_señal.conectar(Enemigos::borrar_lista)
     m_señal.conectar(Personaje::eliminar)
  }

  procesar(Evento evento)
  {
    if (tipo_evento == EVENTOS_SALIR)
      m_señal.emitir()
  }

  Señal  m_señal
}


Por supuesto, siempre has de aplicar la lógica en estos casos. Si no vas a necesitar más que un par de estas, te recomiendo usar el ejemplo de Hugo ( se trata de un sistema de señales básico), y te evitarás tener que pegarte con Boost (a mi me llevo un montón de tiempo averiguar cuales eran los archivos necesarios. No querras usar todo, ya que son casi 100MB de código. De hecho yo tuve que hacer un programa que calculara los ficheros necesarios para compilar boost-signals).
Por otro lado, boost-signals tiene más funcionalidad y es más extensible)

PD: Aquí tienes nuestro archivo de habilidades
Avatar de Usuario
Juanxo
 
Mensajes: 437
Registrado: Sab Ene 31, 2009 2:34 am
Ubicación: Madrid(España)

Re: Problema detectando clicks

Notapor RafaG » Mar Oct 05, 2010 7:30 pm

Bueno, gracias, creo que lo voy a hacer de modo fácil, sin señales.

Saludos y gracias.
RafaG
 
Mensajes: 20
Registrado: Lun Abr 05, 2010 10:57 am


Volver a Sobre las bibliotecas multimedia

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 1 invitado