20 de Diciembre, 2005

[PJRS] Gestión de eventos en SDL

Esta anotación forma parte de la serie Programando Juegos con Ruby y SDL.

La de hoy creo que va a ser una de las partes más feas de explicar, pero es necesario que sepamos como gestionar los diferentes eventos que puede capturar la librería SDL porque van a ser la forma de obtener información del usuario durante el juego.

Con esta parte ya tendremos una plantilla completa para nuestros desarrollos con Ruby y SDL.

En la entrega anterior nos quedó pendiente la gestión de eventos. Para ello vamos a emplear dos objetos principalmente: SDL::Event (pese a que el autor de SDL Ruby, Ippei Ohbayashi, recomienda usar SDL::Event2) y SDL::Key.

Ya podemos empezar a hablar del bucle del juego, aunque en realidad, y por ahora, solo es un bucle y no un juego :P.

Vamos a emplear una variable que nos indicará cuando se ha dado una de las condiciones de terminación que comentamos en la primera entrega (se pulsa ESC, el jugador pierde, el jugador abandona, etc.).

Dentro de ese bucle realizaremos la captura de eventos, distinguiendo dos fases:

  • Procesar eventos pendientes: tenemos que estar al corriente de los eventos de los que nos va informando SDL, se se van almacenando en una cola. Estos eventos serán de distintos tipos (SDL::Event::KEYDOWN, SDL::Event::KEYUP, SDL::Event::MOUSEMOTION, etc.), aunque solo debemos procesar obligatoriamente uno de ellos: SDL::Event::QUIT (por ejemplo, el usuario cierra la ventana del programa). Para el resto será suficiente con que los quitemos de la cola.
  • Ver el estado de la teclas: cuando trabajamos con el teclado no nos interesa realmente ver cuando se pulsó o se soltó una tecla, sino si una tecla está pulsada. Esto es algo que los eventos de SDL no nos dirán, pero que gracias a SDL::Key podremos saber (el estado de cada tecla, sin preocuparmos de los eventos que propician los cambios de estado). Hay casos especiales que no se contemplarán aqui. Por ejemplo: si se pulsa ESC, salimos (no nos interesa saber si está pulsada la tecla, solo si se ha pulsado).

Ya podemos empezar a definir nuestro bucle:

# el bucle *******************************

# para procesar eventos
ev = SDL::Event.new

listo = false
while ! listo

	# primero: eventos inmediatos
	# mientras hayan eventos, los procesamos
	while ev.poll != 0
	
		# hay que procesar QUIT
		if ev.type == SDL::Event::QUIT
			puts "quit!\n";
			listo = true
		end
		
		# si se pulsa ESC, nos vamos
		if ev.type == SDL::Event::KEYDOWN &&
			ev.keySym == SDL::Key::ESCAPE
			
			# se ha pulsado una tecla y
			# además es ESC!
			puts "ESC!\n";
			listo = true
		end
	end
	
	# segundo: teclas pulsadas
	# hay que hacer un scan lo primero de todo
	SDL::Key.scan
	
	# verificamos algunas teclas
	if SDL::Key.press?(SDL::Key::UP)
		puts "se pulsa arriba!\n";
	elsif SDL::Key.press?(SDL::Key::DOWN)
		puts "se pulsa abajo!\n";
	end
	
	if SDL::Key.press?(SDL::Key::RIGHT)
		puts "se pulsa derecha!\n";
	elsif SDL::Key.press?(SDL::Key::LEFT)
		puts "se pulsa izquierda!\n";
	end
end

Parece muy sencillo, pero hay algunas cosas jugosas para comentar.

Primero creamos un objeto SDL::Event, que usaremos todo el tiempo. No hace falta crear un objeto en cada vuelta de bucle, así que la llamada al método constructor la hacemos fuera del while principal.

En la primera fase, mientras hayan eventos pendientes, los procesamos. Cada vez que se llama al método poll, se devuelve 0 o 1 dependiendo de si hay o no un evento pendiente, y se saca el evento de la cola para procesarlo. Aunque pueda parecer que con esta estructura el programa se quedará congelado en ese bucle si no dejan de llegar eventos, esto no pasará. El programa es muy rápido procesando la cola y no tendremos problemas para hacer otras cosas además de vaciar la cola ;).

En esta fase lo esencial es verificar el evento SDL::Event::QUIT, poniendo la variable de terminación del bucle a cierto si es necesario. Podemos verificar que este caso se da cuando se cierra la ventana del programa (he empleado puts para mostrar mensajes en consola).

Luego verificamos la pulsación de ESC, mirando si se pulsa una tecla (el tipo de evento) y además si es SDL::Key::ESCAPE (dato que hay en un evento de ese tipo).

En la segunda fase miramos si alguna tecla interesante está pulsada. Si llamamos al método press? sin llamar antes a scan, tendremos problemas (le he mandado un informe de error a Ohbayashi y me ha contestado que estará arreglado para la próxima versión).

Por ahora esta segunda fase tiene poca importancia, solo la he puesto para que jueguemos un poco con ella. Aún así es interesante entender que se puede dar el caso de pulsar, por ejemplo, ARRIBA y DERECHA a la vez, pero no ARRIBA y ABAJO (damos prioridad a ARRIBA en ese caso). He empleado la estructura if ... elsif de Ruby, que es muy cómoda para este caso.

Ya tenemos al 50% más o menos el bucle general del juego que describimos en la primera anotación de la serie. Ahora tenemos que empezar a ver fundamentos de animación y técnicas de dibujo, pero eso ya lo dejo para la próxima entrega ;).

Anotación por Juan J. Martínez, clasificada en: programming, sdl, ruby.

Hay 3 comentarios

Gravatar

En el elsif, la condición debería ser

SDL::Key.press?(SDL::Key::UP)

	if SDL::Key.press?(SDL::Key::UP)
		puts "se pulsa arriba!\n";
	elsif SDL::Key.press?(SDL::Key::UP)
		puts "se pulsa abajo!\n";
	end

Si bien es un detalle para cuando se publique el articulo completo sirve la corrección. :-)

Muy interesantes los articulos

por Des, en 2005-12-21 13:58:34

Gravatar

Quise decir...
En el elsif, la condición debería ser

SDL::Key.press?(SDL::Key::DOWN)

OT: sugerencia para el sitio "Previsualisación de los comentarios" ;-P

por Des, en 2005-12-21 14:01:11

Gravatar

Menos mal que alguien hay por ahí que lee el código.

Me apunto tu sugerencia (aunque por ganas creo que va a ser que no :D).

¡Gracias!

por Juanjo, en 2005-12-21 14:05:15

Los comentarios están cerrados: los comentarios se cierran automáticamente una vez pasados 30 días. Si quieres comentar algo acerca de la anotación, puedes hacerlo por e-mail.

Algunas anotaciones relacionadas: