Tutorial SDL - Primera parte

(La segunda parte de este tutorial esta aquí)

Indice



Bienvenida

Hola, esta será el primer articulo de una serie de tutoriales sobre SDL y la programación de Videojuegos.
Los primeros tutoriales estarán orientados a los mas nuevitos en el tema, y la intención es de alguna forma atenuar los primeros dolorosos pasos en la programación de juegos.
Estoy asumiendo que el lector sabe programar en C usando el paradigma estructurado, si no les recomiendo que lean algún buen libro de C. No voy a explicar nociones ni algoritmos básicos de programación, pero si voy a dar estructuras de datos y algunos algoritmos específicos para juegos.

Así que, ¡Bienvenidos a bordo, futuros programadores de juegos!

¿Por qué SDL?

Primeros pensemos a la inversa, ¿Qué necesito para que un video juego funcione?. Veámoslo desde el punto de vista de un jugador: Yo pongo el CD de mi juego favorito en mi PC y la magia comienza, primero un video introductorio me posiciona en el mundo fantástico donde toda la acción va a ocurrir; la música me envuelve y el conjunto imagen y sonido cumplen con su cometido de hacerme olvidar que estoy frente a una PC. Comienzo a jugar y el teclado y el mouse hacen su trabajo de forma impecable, el personaje que estoy manejando hace exactamente lo que yo le estoy ordenando...

Examinemos un poco esto que acabo de decir y lo que implica; necesitamos una librería capaz de mostrar gráficos, reproducir sonidos y capturar la entrada del teclado y mouse de forma eficiente.

SDL (Simple DirectMedia Layer ) es una librería escrita en C que proporciona acceso sencillo y de bajo nivel a los recursos del sistema tales como la placa de video, placa de sonido, mouse, teclado, joysticks; mas generalmente todo lo que necesitamos para hacer un videojuego.

Te vuelvo a preguntar: ¿Por qué SDL?
Primero, SDL es realmente sencillo de usar para un newbie (y lo suficientemente poderoso para un programador avanzado), los nombres de las funciones son muy intuitivas y con unas pocas líneas de código uno puede ver resultados sorprendentes en pantalla.
Segundo, SDL es portable; o sea que el mismo código que funciona para Windows, funciona para GNU/Linux y para Mac.
Y Tercero, se puede usar OpenGL para renderizar nuestro juego. Esto nos va a permitir poder mostrar gráficos en 3 dimensiones y tener acceso directo al hardware de la placa de video.

Me imagino que debes estar muy entusiasmado y pensando “¿Cómo hago mi juego de ROL?”; ya vamos a llegar a eso, pero primero hay que descargar las librerías y preparar el entorno, así que sin mas, sigamos leyendo.

Descargando SDL

SDL se descarga desde su pagina web www.libsdl.org donde tenemos una introducción de lo que es y un panel de navegación a la izquierda.
En la parte de “Download” apretamos donde dice “SDL 1.2”, bajamos un poco la página hasta la sección “Development Libraries”.
Antes de seguir, te voy a explicar como instalar y setear el entorno de trabajo, tanto como windows como para Linux, si sos un usuario de windows, seguí leyendo a continuación. Si preferís usar Linux, saltéate la sección de windows.

windows

Desde esta página tenemos varias opciones para descargar, lo que descargaremos serán los binarios de desarrollo, estos vendrán con todas las cabeceras para desarrollar (archivos .h) y las librerías (archivos .lib y .dll).
Aquí esta el link de los releases: http://www.libsdl.org/download-1.2.php

¿Qué es un .lib y para que sirve?

Como sabrán, el mundo windows esta lleno de DLLs, estos archivos son pequeños rejuntes de funciones reutilizables; por ejemplo existe en Windows una DLL que indica a cada ventana como debe dibujarse la barra de titulo (además de otras cosas), como esto siempre es igual para casi todos los programas de Windows sería un desperdicio de espacio tener ese código en cada aplicación, así que la pusieron en un solo archivo.

Cuando uno tiene que hacer un programa que utiliza alguna función contenida dentro de una DLL, uno debe indicarle al compilador en que parte del archivo se encuentra, algo así como un índice; ese índice es el archivo LIB.

Una vez descargado los archivos, extraerlos. Yo los voy a extraer en el disco C.
Si exploramos la carpeta creada deberá haber quedado así:
    C:\SDL-1.2.13
C:\SDL-1.2.13\lib
C:\SDL-1.2.13\include

Después volveremos con el contenido de cada carpeta.
Si no queres instalarlo (ni saber como) en Linux... salta a la página “preparando el entorno”.

Linux

Dependiendo de la distribución de Linux que tengas; podes instalar el paquete que corresponda a la librerías de desarrollo de SDL, explicar esto para cada distribución sería un trabajo tedioso; entonces en vez de eso voy a explicar como descargar los archivos fuentes y compilarlo, ya que esto funciona para todas las distribuciones de Linux y otros sistemas operativos unix.
El código fuente se descarga en la sección “Downloads”, al principio de la página donde dice “Source Code”; o aquí les dejo el link directo del fuente para descargar: http://www.libsdl.org/release/SDL-1.2.13.tar.gz

Vamos a descargarlo en nuestro $HOME en mi caso en /home/david/

Ahora sigamos los siguientes pasos.
Primero entremos como administrador del sistema:
nota: cuando veas el signo $ o el # tenes que escribir esa linea en la consola. El $ es como tu usario, y el # es como root.
$ su -
password: ...

Yo generalmente cuando compilo e instalo un software lo instalo en /usr/local/, el cual voy a usar ahora. Entonces hacemos un cd a ese directorio y desenpaquetemos la librería.
# cd /usr/local/
# tar -zxvf /home/david/SDL-1.2.13.tar.gz

Esta última acción nos va a dejar todo listo para comenzar la compilación.

Preparando el entorno

Windows

Utilizaremos el Microsoft Visual C++ 6.0 como IDE de cabecera; entonces comencemos.
Una vez que tengamos abierto el IDE haremos clic en File -> New y seleccionaremos Win32 Application, dejamos todo lo demás como está y le ponemos el directorio y el nombre que queramos.
Y pulsamos “OK”.

Imágen
y le damos “Finish”.
Un pequeño informe nos dirá que fue lo que hizo el entorno por nosotros y aceptamos tranquilamente.
Ahora tenemos un proyecto limpio por donde empezar; primero Projects -> Settings
Y en el Tab "C/C++" en la categoría “Code Generation” ponemos como esta la imagen.

Imágen

Y en categoría “Preprocessor”, agregar un directorio adicional para el compilador, que fue donde instalamos SDL apuntando al directorio donde están las cabeceras.

Imágen
En el Tab “Link”, la categoría “Input” agregar 2 “library modules”
sdlmain.lib y el sdl.lib que están en el directorio donde instalamos SDL bajo la carpeta “lib”.

Imágen
Y le damos “OK”

Por último, necesitamos un archivo en el cual escribir, hacemos clic en File -> New.
Elegimos “C++ Source File” y le ponemos el nombre de nuestro agrado.

Imágen
“OK”

Linux

Aquí realmente no hay mucho que preparar, primero comprobamos que tengamos instalado un compilador de C, para eso escribimos lo siguiente desde la línea de comandos:
# gcc -v
Si aparece información sobre el compilador, estamos bien.

Luego entramos al directorio donde están los fuentes de la librería SDL.
# cd SDL-1.2.13

Que es el directorio que creo el comando tar y a continuación ejecutamos los tres comandos que comúnmente sirven para compilar e instalar un soft en GNU/Linux.
#./configure
#make
#make install

Esto no debería tener ningún problema, si lo tienen, entonces les recomiendo que busquen ayuda en el foro de SDL, el cual siempre hay personas contribuyendo para solucionar los problemas de los usuarios, así como tips de programación y demás: http://www.libsdl.org/mailing-list.php

para testear que todo haya ido bien vamos a ejecutar el siguiente comando como un usuario común; así que primero nos deslogeamos de 'root'
# exit
$ sdl-config --version

Este comando nos mostrará la versión de SDL instalada en nuestro sistema: la cual es "1.2.13". Este comando nos servirá mas adelante para poder compilar los programas que hagamos en Linux.

Creamos el directorio que mas les guste en nuestro $HOME donde vamos a poner el código fuente para ir probando lo que vayamos haciendo y creamos un archivo con extensión .c ó .cpp y lo editan con su editor preferido (yo personalmente uso el vim).
$ cd 
$ mkdir tutorial_sdl
$ vi hola_mundo.cpp


Hola Mundo!

Esto es igual, sin importar el Sistema Operativo que usen, aquí se ve bien la potencialidad de SDL en cuanto a lo multiplataforma.
Ahora vamos a escribir el primer ejemplo para ver si todo estuvo bien seteado y para que tengan un primer vistazo de lo fácil que es crear una ventana con SDL.
Lo que vamos a hacer es una pequeña ventana a la cual le vamos a poner un titulo explicativo.
Así que sin importar que estén usando copien el contenido del siguiente archivo en su editor favorito.
hola_mundo.cpp

windows

Presionemos el icono del signo de admiración (CTRL.+F5) para ejecutar el programa, antes de ejecutarlo el IDE se dará cuenta de que no lo compilo, entonces primero lo compilará y después lo ejecutara.

Linux

$ g++ hola_mundo.cpp -o holamundo `sdl-config --cflags --libs`
$ ./holamundo

Primero explicaré como compilar el fuente en un entorno Linux, y después paso a paso detallaré que es lo que hace independientemente del entorno.
El comando g++ es el que se usa para compilar fuentes C++ en un entorno GNU/Linux.
Este comando acepta muchisimos parametros, a continuación les explicaré los que estamos usando:
El g++ espera el nombre del archivo fuente a compilar; hola_mundo.cpp en este caso.
-o le indica el archivo a generar (la letra 'o' es de output --salida en inglés--); holamundo en el ejemplo.
-I en donde debe buscar por cabeceras adicionales.
-L en donde debe buscar por librerias adicionales.
y por último -l seguido de un nombre le dice que librerias adicionales queremos incluir para armar el ejecutable.
Los parametros que nos faltan se lo pasamos invocando un segundo comando:

¿Que es el sdl-config?

El comando sdl-config, le indica al compilador C de donde puede obtener las cabeceras para compilar (--cflags) y tambien le dice que opciones debe usar para la link-edición y que librerías debe linkear (--libs).
Si son curiosos pueden ejecutar el comando por el shell y ver la salida que muestra.
$ sdl-config --cflags
-I/usr/local/include/SDL -D_REENTRANT
$ sdl-config --libs
-L/usr/local/lib -Wl,-rpath,/usr/local/lib -lSDL -lpthread


Ahora vamos a explicar paso a paso que es lo que hace cada llamada

SDL_Init (SDL_INIT_VIDEO)
Le indica a SDL que inicie la librería con soporte para video: SDL_INIT_VIDEO; también se le pueden indicar módulos adicionales y algunas opciones de inicio.

SDL_SetVideoMode(400, 400, 16, 0)
Esta función crea una ventana con el ancho (400) y alto (400), la profundidad de color (16) y flags adicionales (0) para indicar que no utilice ningún flag y ejecutando el programa en una ventana.

“ancho y alto”
son auto explicativos, los valores están en pixels.

“profundidad de color”
Como ustedes saben, la unidad mínima de información que puede mostrar el monitor es un Píxel. La profundidad de color es la cantidad de bits que tiene asociado ese píxel. Por ejemplo si yo utilizo 8 bits de profundidad, solamente puedo mostrar 256 colores en un solo píxel. En los juegos modernos se utilizan entre 16 a 32 bytes de profundidad.
Si la placa de video no soporta la cantidad de profundidad especificada, SDL va a reintentar con una profundidad soportada y convertirá todas los gráficos a esta profundidad. Esto característica puede ser desactivada, si se desea pasándole SDL_ANYFORMAT como parámetro en la parte de flags

SDL_GetError()
Cuando una función de SDL devuelve un código inválido, la forma de conocer que ocurrió es llamando a esta función que nos devuelve un char* con un mensaje descriptivo de lo ocurrido.
Supongamos que hayamos llamado:
SDL_SetVideoMode(400, 400, 16, SDL_ANYFORMAT)
En este caso es posible que la placa de video no soporte 16 bits de profundidad y devuelva error, en ese caso la función SDL_GetError() nos mostrará que fue lo que paso.

SDL_WM_SetCaption("Hola mundo!", "Hola Mundo!")
Le pone titulo a la ventana y el nombre del icono de la aplicación.
Esto obviamente es necesario si nuestro juego se va a ejecutar en modo ventana, si vamos a usar modo “full screen”, esta función no es necesaria.

SDL_Delay(5000)
Es usado para detener la aplicación por la cantidad de milisegundos deseados.
En este caso detenemos la pantalla por 5 segundos.

SDL_Quit()
Cuando terminamos de usar la librería, hay que hacer limpieza y devolver los recursos obtenidos por la aplicación.

Una mejor y mas detallado explicación de estas funciones y todas lo que pueden hacer con SDL la pueden encontrar en la documentación. Esta se puede descargar de la página oficial en la parte de Documentación

Eventos


Ya los estoy escuchando diciendo: “la ventana no se mueve, se queda quieta y no responde a los botones, no se minimiza y no se cierra”
Si, ya se. Un paso por vez.
Les explico, las ventanas en cualquier escritorio se manejan por eventos, el usuario le envía a la ventana eventos; existen cientos de eventos que puede una ventana manejar, pero no todos nos sirven para juegos.
Los eventos que recibe la ventana del usuario que vamos a utilizar en la mayoría de los casos son: los eventos del movimiento del mouse, las teclas presionadas del teclado; cuando el usuario presiona para cerrar la ventana, para minimizarla o cuando la quiere redimensionar.

Para manejar los eventos en nuestra aplicación, vamos a tener un loop infinito que va a chequear si tiene eventos que procesar, esos eventos son informados por el manejador de ventanas a nuestra aplicación y nosotros los capturamos y decidiremos que hacer con ellos, los podemos ignorar o procesar.

Sin mas charla vamos a un ejemplo.

Hola Mundo 2!

hola_mundo2.cpp

Bueno!, ahora puedo mover la ventana por la pantalla, minimizarla y cerrarla con el botón de cerrar.
Pero ¿Qué son esas líneas de mas?
Ahora paso a explicar que es esto:

//La estructura de eventos de SDL.
SDL_Event ev;

while( true ) {
while( SDL_PollEvent( &ev ) ) {
if( ev.type == SDL_QUIT ){
printf( ">El usuario quiere salir.\n" );
printf( ">SDL Terminado.\n" );
return 0;
}
}
}

SDL_Event es la estructura de SDL en la cual guarda la información de los eventos del administrador de ventanas; SDL usa una estructura propia para poder ser multiplataforma, ya que las diferencias entre windows, Linux y Mac son manejadas transparentemente para nosotros.

SDL_PollEvent(&ev)
Se encarga de procesar todos los eventos relacionados con la aplicación y completa por cada uno la estructura SDL_Event. Cuando no hay mas eventos que manejar devuelve FALSE.

SDL_QUIT.
Después que SDL_PollEvent transforma el evento en un SDL_Event; nosotros podemos saber que tipo de evento se disparo con solo inspeccionar la variable “type”.
Uno de los tipos de eventos es el SDL_QUIT, el cual, como el nombre lo dice, nos avisa que el usuario quiere salir.

Tengamos en cuenta que nosotros como programadores estamos encargados de asignar a cada evento una respuesta acorde, si nosotros no manejamos adecuadamente el evento de salir, la aplicación no va a salir.


Despedida


Uff! Cuanto que hicimos con tan poco código, pero los conceptos para asimilar son muchos.
El desarrollo de juegos es un área que requiere aprender muchos conceptos nuevos y algunas nociones de cómo funciona la PC de bajo nivel, para poder entender ciertas cosas que estamos haciendo.
Programar juegos no es una tarea sencilla, pero si es altamente gratificadora; ya que constantemente están saliendo nuevas tecnologías para desarrollar y tenemos la ayuda de librerías como SDL que nos hacen el trabajo un poco mas cómodo.

Por hoy dejamos acá, ya sabemos como crear una ventana, primer paso para cualquier aplicación, y como manejar algunos eventos.
Como tarea les dejo que lean la documentación y busquen que otros tipos de eventos soporta SDL, ya que les va a ser útil cuando retomemos el tema de los eventos.

Para la próxima, dejaremos de lado por un momento los eventos y comenzaremos a ver la forma de mostrar gráficos en pantalla y como crear el efecto de animación.

Cualquier comentario, duda o sugerencia que tengan; no duden en enviarme un e-mail a nesdavid[arroba]gmail com .

Saludos.
David Roguin.