Bienvenido(s) de nuevo mi(s) querido(s) lector(es). En el capítulo de hoy vamos a echar un pequeño vistazo a las librerías SFML, Simple Fast Multimedia Library. En el primer post dijimos que no íbamos a ver cómo inicializar cosas como las SDL y tampoco es la intención de esta entrada, pero considero adecuado dar un ligero vistazo a cómo se hace para poner un sprite en pantalla, hacer sonar un efecto de sonido, etc con las SFML. Tampoco vamos a ver cómo se hace todo en sus múltiples variantes, para eso mejor mirar la sección de tutoriales de la página de las SFML.
Las librerías SFML son un equivalente de las SDL, sólo que están programadas en C++ (en contraposición al C de las SDL) y orientadas a objetos. Dispone de varios "bindings" para diferentes lenguajes de programación aparte de C++ y está estructurado en varios módulos (gráficos, sonido, etc). Para dibujar usa openGL en vez de una solución por software como hacen las SDL (aunque parece ser que en la siguiente versión estas librerías también usarán openGL). Esto implica que con las SFML tenemos efectos como rotaciones, escalados, semitransparencias y demás de serie, a diferencia de las SDL donde no podemos hacer ni un simple "flip" con lo que nos ofrecen las librerías. Las SFML también se pueden usar como las SDL como un sistema multiplataforma para inicializar openGL y lidiar con el teclado y demás, si lo estimamos oportuno. Gracias a su diseño modular podemos usar sólo lo que necesitemos.
Las SFML incluyen librerías para hilos, teclado, joystick, ratón, crear ventanas, pintar gráficos, reproducir música (en formato OGG), reproducir efectos de sonido, etc. Todo multiplataforma (Windows, Linux y Mac OS X). Aquí vamos a ver la última versión estable, la 1.6. La versión 2.0 se supone que debe salir en un futuro más o menos próximo con interesantes novedades en el aspecto gráfico, entre otros un mecanismo para hacer batching. La versión 1.6 no hace esto, aunque para un juego como Culebrilla no supone ninguna diferencia.
Bueno, empecemos: nuestra aplicación comienza con un simple main() corriente y moliente. Para ver algo debemos crear un RenderWindow, que es la clase que usan las SFML como punto de partida de su infraestructura. Al crearlo podemos indicar los típicos parámetros de tamaño, tipo de ventana, si es a pantalla completa o no, esperar el retrazo vertical, etc.
Después de instanciar un RenderWindow ya podemos entrar en nuestro famoso bucle principal. Si la instancia de RenderWindow se llama "renderWindow", el código podría quedar así:
Aquí vemos que tenemos un while que se ejecuta mientras tenemos abierto renderWindow. Después viene la parte donde somos buenos invitados del SO y atendemos a sus eventos: mientras RenderWindow::GetEvent() nos devuelva true tenemos un evento pendiente del SO, sino ya hemos cumplido y salimos del while para hacer nuestras cosas. En este while de los eventos miramos si el evento es el cerrar la ventana (ya sea pulsando el botón o con alguna combinación como ALT + F4) o ha pulsado la tecla ESC. Si es así cerramos renderWindow, lo cual hace que salgamos de la aplicación. Si no es uno de estos eventos se lo pasamos a nuestro juego (la instancia "game") para que pueda responder correctamente al teclado, joystick o lo que considere adecuado.
while(renderWindow.IsOpened()) { sf::Event currentEvent; while(renderWindow.GetEvent(currentEvent)) { // El usuario ha cerrado el programa? if(sf::Event::Closed == currentEvent.Type || (sf::Event::KeyReleased == currentEvent.Type && sf::Key::Escape == currentEvent.Key.Code)) { renderWindow.Close(); // Cerramos la aplicacion } else { // Pasamos el evento a nuestro juego (teclado, etc) game.processEvent(currentEvent); } } // Actualizamos nuestro estado game.update(renderWindow); // Pintamos los graficos game.draw(renderWindow); // Mostramos lo que hemos pintado en el paso anterior renderWindow.Display(); }
Aquí vemos que tenemos un while que se ejecuta mientras tenemos abierto renderWindow. Después viene la parte donde somos buenos invitados del SO y atendemos a sus eventos: mientras RenderWindow::GetEvent() nos devuelva true tenemos un evento pendiente del SO, sino ya hemos cumplido y salimos del while para hacer nuestras cosas. En este while de los eventos miramos si el evento es el cerrar la ventana (ya sea pulsando el botón o con alguna combinación como ALT + F4) o ha pulsado la tecla ESC. Si es así cerramos renderWindow, lo cual hace que salgamos de la aplicación. Si no es uno de estos eventos se lo pasamos a nuestro juego (la instancia "game") para que pueda responder correctamente al teclado, joystick o lo que considere adecuado.
Una vez atendidos los eventos del SO pasamos a ejecutar código del juego: actualizamos el estado y pintamos los diferentes elementos del juego. A estos dos métodos le pasamos la instancia renderWindow, ya que el método update() lo usará para calcular el tiempo (usando RenderWindow::GetFrameTime()) y el método draw() lo usará como destino de los gráficos, que vamos a ver a continuación.
La parte gráfica se base en las clases Image y Sprite, aunque hay más (dibujado de formas geométricas, texto, efectos de postproceso, etc). La primera es la encargada de guardar la información de las imágenes (normalmente cargada de un fichero gráfico como puede ser un png). La segunda es la que usa la información de Image y lo pinta a un RenderTarget. En nuestro caso es la instancia de RenderWindow, que deriva de RenderTarget. Esto implica que podemos tener una instancia de Image que es usada por múltiples instancias de Sprite, pudiendo cada uno de estos presentarla de una forma diferente (con su propio escalado, rotación, etc).
Cargar un fichero gráfico es tan sencillo como lo siguiente:
Hay que tener en cuenta que la clase Image no controla la carga de ficheros repetidos. Es decir, si yo creo 5 instancias de la clase Image y en todas cargo el fichero "background.png", este fichero se habrá cargado 5 veces, consumiendo 5 veces la memoria correspondiente. Evitar cargar más de una vez un fichero queda en manos del cliente de la clase.
sf::Image img;
img.LoadFromFile("graphics/head.png");
Hay que tener en cuenta que la clase Image no controla la carga de ficheros repetidos. Es decir, si yo creo 5 instancias de la clase Image y en todas cargo el fichero "background.png", este fichero se habrá cargado 5 veces, consumiendo 5 veces la memoria correspondiente. Evitar cargar más de una vez un fichero queda en manos del cliente de la clase.
Respecto a la clase Sprite su funcionamiento también es sencillo. Por ejemplo para dibujar lo que hemos cargado en el ejemplo anterior haríamos lo siguiente, usando también la instancia de RenderWindow que hemos visto antes en el bucle principal:
Esto dibujaría lo que tengamos en la variable img en la esquina superior izquierda. El sistema de coordenadas de la parte gráfica de las SFML es como vimos en la entrada sobre la actualización del estado. El ejemplo es de una pantalla de 800x600 píxeles:
sf::Sprite sprite(img); sprite.SetPosition(0, 0); renderWindow.Draw(sprite);
Esto dibujaría lo que tengamos en la variable img en la esquina superior izquierda. El sistema de coordenadas de la parte gráfica de las SFML es como vimos en la entrada sobre la actualización del estado. El ejemplo es de una pantalla de 800x600 píxeles:
La clase Sprite dispone además de varios métodos que son muy útiles para programar juegos: flip (efecto espejo) tanto en X como en Y, fijar la posición del sprite o moverlo, rotarlo, escalarlo, cambiar el tipo de blending, definir un rectángulo para dibujar sólo una parte del Image del Sprite, etc. Todo bien explicado en la documentación en su página web.
Para reproducir música o efectos de sonido la cosa es más o menos la misma: instanciar la clase correspondiente e indicarle la ruta del fichero a cargar. Además de cargar los datos de un fichero, las SFML ofrecen también métodos para cargar de un buffer en memoria, por ejemplo si queremos hacer algún sistema de paquetes para proteger nuestro contenido. Esto se aplica a todas las clases que cargan de un fichero (gráficos, música, etc).
No creo que merezca la pena ahondar más en las SFML, ya iremos viendo su uso con más detalle a lo largo del desarrollo de Culebrilla. Como colofón de esta entrada tenéis un ejemplo muy sencillo para ver lo fácil que es usar las SFML en la documentación.
¡Nos vemos en la siguiente entrada!

No hay comentarios:
Publicar un comentario