Anda di halaman 1dari 10

Tutorial SDL - Segunda parte

(La tercera parte de este tutorial esta aquí)

Indice

Introducción
SDL y 3rd Party Libraries
¿Qué es la SDL_Image?
A Instalar
Windows
DLLs
Linux
Setear el entorno
windows
Linux
Manos a la obra!
Windows
Linux
Color Key
Rojo, Verde y Azul
Transparencias
Transparencia por pixel y transparencia por imagen
Por Pixel
Por Imagen
Optimización
En el tintero
Alpha en vez de Color Key
Ordenar
Despedida

Introducción
Buenas, esta es la segunda parte del tutorial de SDL, vamos a seguir de donde dejamos la vez pasada, así que
si no leíste, aquí te dejo el link. Básicamente la vez pasada hablamos sobre como descargar e instalar SDL en
Win y GNU/Linux y como crear una pequeña y muy básica aplicación.

En esta parte vamos a ver cómo poner una imagen en pantalla; sí, la idea es muy sencilla, primero
necesitamos una imagen en un formato conocido, llámense BMP, JPG, PNG, etc y después lo que hacemos es
cargarlo con SDL y decirle que parte de la imagen cargada queremos mostrar en que parte de la pantalla.

Parece fácil, ¿no? bueno... en realidad lo es. ;-)

SDL y 3rd Party Libraries


1
Antes de meter mano en el código vamos a aclarar algunas cosas sobre SDL.

SDL nativo solamente tiene funciones para cargar archivos de tipo BMP, entonces ¿cómo es que puedo
cargar cualquier tipo de formato de imagen?

Afortunadamente, seres humanos con la misma incógnita que la nuestra, escribieron una librería llamada
SDL_Image que nos permite hacer eso.

¿Qué es la SDL_Image?
La SDL_Image es lo que se llama una 3rd Party Library, esta librería se integra muy bien con SDL (por eso el
prefijo SDL) y maneja los mismos tipos de datos de tal manera que parece natural el uso de la SDL_Image
dentro de SDL.

SDL es una librería de bajo nivel con mucha flexibilidad que nos permite hacer muchas cosas y no usarla
solamente para juegos, pero este bajo nivel hace que sea necesario crear librerías que nos permita hacer
ciertas cosas comunes mas fácilmente. Por ejemplo, SDL tiene manejo de audio nativo, pero es muy
complicado hacerlo funcionar, por eso crearon la SDL_Mixer, que es una librería que funciona como
agregado al sistema de audio nativo y nos permite, con una API sencilla, realizar las funciones mas comunes
de un juego, por ejemplo cargar un archivo que contiene la música del nivel y reproducirla infinitamente.

Para cerrar el tema de las librerías, y pasar a los que nos interesa, voy a listar las librerías mas utilizadas y mas
necesarias para realizar un juego:

SDL_Mixer: Como dije antes sirve para agregar un sistema de audio, nos permite la carga de archivos tipo
WAV y OGG y la reproducción de estos, también nos permite hacer algunos efectos de panning.

SDL_Net: Esta es indispensable si estamos pensando en MMO, la SDL_Net nos permite la comunicación vía
red de manera TCP y UDP.

SDL_TTF: La True Type Font nos da la posibilidad de mostrar texto en la pantalla usando un archivo ttf para
renderizar las fuentes.

Como último comentario les quiero decir que estas librerías son 100% multiplataforma, y si miran
atentamente se habrán dado cuenta que uno de los creadores de estas librerías es el propio autor de SDL, lo
cual es (de cierta forma) una garantía de calidad.

Les recomiendo que de vez en cuando visiten la página de librerías por si hay alguna librería nueva que les
pueda ser útil; siempre miren si están portadas a los sistemas que quieren que su juego corra.

A Instalar
En la página de SDL_Image y al igual que en la mayoría de estas librerías van a tener la documentación para
descargar, la cual es muy recomendable; el código fuente, es la elección si quieren compilarlo; y los binarios,
recuerden que si quieren bajarlos para programar siempre busquen los que dicen *-devel-*.

Windows

2
Al abrir el archivo SDL_image-devel-1.2.6-VC8.zip van a ver 2 directorios que le deberían importar, uno es el
include y el otro es el lib. Lo que les recomiendo es que copien el contenido del directorio include dentro del
directorio include de la instalación de SDL y con el archivo .lib que esta dentro del directorio lib, de esta
manera no van a tener que agregar directorios en los seteos del proyecto. No se preocupen que no hay
archivos repetidos.
Con todas las DLLs que están en ese directorio ya les digo mas adelante que pueden hacer con esas. ;-)

DLLs

Aquí vamos a hacer un alto y explicar lo de las DLL; dentro del directorio libs verán unas cuantos archivos
DLL.
¿Qué hacen acá? se preguntaran, lo que hacen ahí es lo siguiente: La SDL_image usa librerías que no son
propias de la SDL_image; por ejemplo la librería zlib, (zlib1.dll) que es una librería para comprimir archivos
(algo así como el zip), y otras librerías para manejar los distintos formatos de archivos de imágenes (png, jpeg,
etc); pero como la SDL_image necesita usar estas estas librerías las incluye en forma de DLL.
Cuando corran un programa hecho con la SDL_image, si Win no encuentra estas librerías se va a quejar. Lo
que necesitan hacer es o copiar las DLL en el mismo lugar donde esta el ejecutable que compilaron, o
copiarlas en algún lugar estándar, por ejemplo C:\windows\system32, o lo que pueden hacer es incluir en su
variable de entorno %PATH% la ruta en donde van a estar.
Lo recomendable es tenerlas en el mismo lugar de su ejecutable, de esta manera estas DLL no van a tener
conflictos con otras DLL que puedan tener el mismo nombre y aparte es una forma de organizarse para el día
que distribuyan su juego, ese día ustedes ya saben que todo lo que necesita otra PC con Win para correr su
juego son esas DLL que están ahí (eso no es del todo 100% verdad pero después le contaré mas).

Linux
Siempre es recomendable que la instalación de los paquetes las maneje su propia distribución, pero ustedes
eligen si quieren bajarlo y compilarlo.
Para instalarlo no tienen mas que bajar el source (yo prefiero en tar.gz) y después hacer los mismos pasos que
hicimos para instalar SDL.
$su -
# cd /usr/local/
# tar -zxvf /SDL_Image-1.2.6.tar.gz
# cd SDL_image-1.2.6
# ./configure
# make && make install
# exit

Setear el entorno
windows
Si me hicieron caso con la instalación (eso espero) todo lo que tienen que agregar en el proyecto es la
SDL_image.lib en las librerías adicionales.

3
Linux
Aquí no hay mucho que preparar, solo hay que verificar que se haya instalado el archivo de cabecera
SDL_image.h y esté la librería libSDL_image.so. En general van a estar en /usr/include/SDL y /usr/lib
respectivamente.
Esta comprobación es realmente innecesaria si el make install no fallo (o si lo instalaron de otra manera
estándar por su distribución)..... pero por las dudas.

Manos a la obra!
Me imagino que estarán impacientes y quieren empezar a codificar, así que abran su editor preferido (seguro
que es el vim) y vamos a hacer lo que dijimos: vamos a cargar una imagen y mostrarla en la pantalla.
Siempre que agreguen una nueva librería de SDL, sigan los pasos descriptos anteriores, ya que siempre tienen
el mismo tipo de información.
Aquí les dejo el link para un zip el cual están todos los ejemplos y las imagenes que vamos a usar; así que
descarguenlo y habran el archivo imagen.cpp, el cual es el primer ejemplo.

Windows
Solo presionen F5.

Linux
$ g++ -o happy imagen.cpp `sdl-config --libs --cflags` -lSDL_image
$ ./happy

4
Vamos a explicar las lineas nuevas de código:

SDL_Surface *screen, *imagen;


SDL maneja todo lo que necesita para dibujar en esta estructura llamada SDL_Surface; una vez que nosotros
tenemos una estructura de este tipo podemos elegir una porción de esta superficie y 'pegarla' en otra
superficie.

SDL_Rect ori, dest;


Esta estructura define un rectángulo.

screen = SDL_SetVideoMode(640, 480, 16, 0);


De esta manera al setear el modo de video, tenemos una estructura que nos define la pantalla; ahora cuando
hagamos un cambio sobre la SDL_Surface screen lo vamos a estar haciendo sobre la pantalla.

imagen = IMG_Load("imagen.jpg");
De esta manera usamos la SDL_Image, fijense que en vez de tener el prefijo 'SDL' tiene el prefijo 'IMG'; esto
siempre es así para las 3rd Party Libraries, cada una tiene un prefijo distinto.
Bueno noten que sencillo cargar una imagen de un tipo cualquiera desde un archivo y tener una
SDL_Surface, a partir de este momento podemos hacer cualquier cosa que querramos con la imagen.
ori.x = 0;
ori.y = 0;
ori.w = imagen->w;
ori.h = imagen->h;

El siguiente paso es decidir que porción de la imagen quiero 'pegar' en la pantalla; para esto defino un
rectángulo.

El eje de coordenadas de la pantalla manejado por SDL es el siguiente:


el (0,0) comienza desde la esquina superior izquierda de la pantalla, todo lo que vaya hacia la derecha, esta
aumentando en el eje X y todo lo que vaya hacia abajo de la pantalla esta aumentando en el eje Y.

Entonces lo que estamos definiendo es un rectángulo con el x e y apuntando en el origen (arriba izquierda) y
le decimos que de ancho (eje x) queremos todo el ancho de la imagen (imagen->w) y de alto queremos todo el
alto que tiene la imagen (imagen->h).
La w y la h vienen del ingles w de ancho (width) y h de alto (height).
dest.x = 0;
dest.y = 0;

Para el destino es distinto, aquí solamente nos interesa desde donde vamos a empezar a 'pegar' la porción
seleccionada; en este caso desde el (0,0).

SDL_BlitSurface(imagen, &ori, screen, &dest);


Ahora lo que hacemos es 'pegar' (Blit) desde la imagen, la porción seleccionada por ori sobre la superficie
screen en el destino indicado por dest.

Si les ayuda (a mi si), pueden pensarlo como si estuviera haciendo un collage; ustedes tienen una cartulina
grandota la cual sería la pantalla: la variable screen de tipo SDL_Surface; y también tienen un montón de
imagenes: cualquier variable de tipo SDL_Surface que hayan creado desde un archivo con la llamada a
IMG_Load().

5
En vez de tener tijera, lo que tienen son rectángulos con los cuales van a definir que parte de la imagen
quieren y en que parte de la cartulina (pantalla) la quieren.
Y en vez de la plasticola tienen el SDL_BlitSurface().

SDL_Flip(screen);
Una vez que su collage esta listo para ser presentado solo tienen que indicar que realmente quieren mostrarlo,
esto es lo que hace el SDL_Flip().

SDL_FreeSurface(imagen);
Cuando terminamos de usar un SDL_Surface hay que liberar los recursos, para eso llamamos a esta función.
Cuidado: Nunca liberen el SDL_Surface que es devuelto por la llamada a SDL_SetVideoMode, esa es
manejada internamente por SDL y liberada tambien por la llamada al SDL_Quit().

Color Key
Esta bien. Eso está muy lindo para imagenes rectangulares, pero yo vi que en todos los juegos en 2D hay
figuras que no son rectangulares, hay personas bien detalladas como por ejemplo el Street Fighter....
Lo que se hace en estos casos es utilizar un color al cual llamaremos 'color key' para el fondo, el cual será un
color que no se utilice en la imagen que queremos mostrar; y el cual se eliminará cuando hagamos el Blit.

Rojo, Verde y Azul


Como ustedes sabrán (si no saben yo les explico), los pixels en pantalla se componen de 3 componentes (hay
un cuarto pero por ahora no le demos bola); estos componentes son el Rojo (Red), el Verde (Green) y el Azul
(Blue), para poder definir el color que un pixel va a tener se tiene en cuenta la cantidad que se usa de estos
componentes para armarlo; por ejemplo si llenamos un pixel con todo Rojo y nada de los otros, obviamente
tendremos un pixel rojo puro en pantalla, lo mismo pasa con los demás componentes.
SDL maneja la cantidad de color de pixels desde un mínimo de 0 hasta un máximo de 255, esto quiere decir
que si yo quiero formar un pixel blanco voy a tener que combinar todos los componentes al máximo
(255,255,255) y si quiero el negro (ausencia de color) voy a tener que usar (0,0,0) para cada componente.

Lo que ustedes tienen que hacer es ponerse de acuerdo con su artista gráfico y proponer un color que no sea
usado en la imagen como fondo por ejemplo un verde puro (0,255,0) o un magenta (255,0,255) o simplemente
un negro puro, como el que uso yo para el ejemplo.
Una vez que ustedes saben el color el cual NO hay que mostrar en la pantalla, hay que indicárselo a SDL de
una manera muy sencilla.

Para demostrar esto vamos a ver otro ejemplo; solo explicaré lo que cambio del ejemplo anterior, así que
asegúrense que lo entienden bien antes de leer este.
El código fuente es color_key.cpp.

SDL_Surface *circulo, *circulo_key;


Una va a contener el circulo sin sacarle el color key y el otro si se lo va a quitar.

SDL_SetColorKey (circulo_key, SDL_SRCCOLORKEY, SDL_MapRGB(circulo_key->format, 0, 0,


0));
Esta es la función que se lleva todos los premios (por ahora); los parámetros son:
circulo_key : es el SDL_Surface al cual le queremos aplicar el "Color Key".
SDL_SRCCOLORKEY: el 2do parámetro son unos cuantos flags, por ahora acuerdense que tienen que poner

6
este.
El 3er parámetro es un SDL_Color, para obtener esta estructura tenemos que hacer una llamada a la función
SDL_MapRGB.
Esta función toma los siguientes parámetros:
circulo_key->format: Es el tipo de formato que utiliza la imagen que cargamos para mostrar los pixels en
pantalla.
lo que sigue son los componentes Rojo, Verde y Azul tal como lo estuvimos discutiendo.

Fijense que después le cambio el valor de la estructuras de origen y destino para mostrar las imagenes donde
quiero que se muestren; siempre que termino de setear el x, y, ancho y alto como lo deseo hago un
SDL_BlitSurface para 'pegar' la imagen origen en destino.
Una vez que termine de formar la imagen como quiero tengo que hacer una llamada a SDL_Flip().
Noten que solamente se llama una vez al SDL_Flip(), ya que solo la queremos para mostrar la imagen una vez
que este lista y no cada vez que le hacemos un pequeño cambio intermedio.

Transparencias
¿Se acuerdan que les comente de un cuarto componente? bueno les presento al componente Alpha, con lo
cual en vez de tener RGB ahora tenemos RGBA.

¿Qué es el componente Alpha?


El alpha se utiliza para saber cuanta transparencia tiene un pixel, un valor de 0 indica transparencia pura y
un valor de 255 indica opaquez pura (para SDL); jugando con lo valores intermedios se va haciendo mas o
menos transparente ese pixel.

Existe una formula para calcular la transparencia de un pixel, la cual no voy a poner acá porque la encuentran
en todos lados y no tiene sentido saberla ya que SDL se encarga de hacerlo por nosotros.
Imaginense que tienen un fondo y delante del fondo están dibujando un vidrio (medio celeste) el cual quieren
que tenga transparencia. Básicamente lo que hace SDL al pixel de la imagen del vidrio es combinarlo con el
pixel que esta detrás, de esa manera, dependiendo del componente alpha que tenga, se combinan los 2 pixels
para formar un nuevo pixel el cual es el resultado del efecto de transparencia.
Esta operación es muy costosa ya que necesita de las 2 operaciones mas lentas de la CPU (que son la
multiplicación y la división).

Transparencia por pixel y transparencia por imagen


Existen 2 maneras de trabajar con componentes Alpha, una es seteando por pixel un componente alpha
adecuado y la otra manera es diciéndole que toda la imagen tenga un componente alpha, igual por cada pixel.

Por Pixel

Cuando decimos que hay que setear un componente Alpha por pixel, no quiero decir que lo vamos a hacer
nosotros programaticamente (aunque podríamos) sino que nuestro artista gráfico va a guardar la imagen que
esta trabajando en un formato que soporte Alpha, por ejemplo PNG.
El archivo ese contendrá información sobre los cuatro componentes y nosotros solamente tendremos que
indicarle a SDL que queremos hacer uso de esa información.

Por Imagen

7
Aquí si vamos a hacer esto programaticamente, lo que queremos hacer es tener una imagen y cambiarle esa
información de forma equivalente en todos los pixels, digamos que queremos que toda la superficie de la
imagen sea un 50% transparente de manera uniforme.

Entonces vamos con otro ejemplo de estas 2 cosas que recién aprendimos:

Una imagen tiene mas transparencia en los bordes que en el centro de la esfera, la otra imagen es la misma
que estábamos usando antes; solamente que le aplicamos un 50% de transparencia.
Además de eso, este ejemplo de yapa tiene algunas nociones de animación.
El código esta en el archivo alpha.cpp.
Ahora viene la explicación de esas lineas de mas.
El código continua a partir del anterior ejemplo:

int lente_x, lente_y, circulo_x, circulo_y;


Para tener control sobre donde están nuestras imagenes

SDL_SetAlpha(lente, SDL_SRCALPHA, 0);


La lente es la imagen que tiene alpha distinto en sus pixeles, para indicarle a SDL que tenga en cuenta la
información alpha usamos esta función;
el 1er parámetro es el SDL_Surface, el 2do unos flags, por ahora debemos saber que siempre es el mismo.
y por último la cantidad de alpha a aplicarle, en este caso el valor es 0 para indicarle que utilice la imagen tal
cual es, sin ninguna modificación.

SDL_SetAlpha(circulo_key, SDL_SRCALPHA, 128);


El circulo no tiene alpha seteado en sus pixels, entonces le decimos que todos van a tener un 50% (la mitad
de 256) de transparencia en toda la imagen. Los parámetros son lo mismos, solo cambia el grado de
transparencia: 128.
Un valor de 255 hará que el circulo se vea exactamente igual que en el ejemplo anterior.
Fijense que primero le aplicamos el color key y después le decimos que grado de transparencia queremos,
haciendo que los efectos se sumen.
for(int i=0; i<1000; i++) {
ori.w = imagen->w;
ori.h = imagen->h;

dest.x = 0;
dest.y = 0;
SDL_BlitSurface(imagen, &ori, screen, &dest);

ori.w = lente->w;
ori.h = lente->h;

lente_x++;
lente_y++;

dest.x = lente_x % 640;


dest.y = lente_y % 480;

SDL_BlitSurface(lente, &ori, screen, &dest);

circulo_x++;
circulo_y++;
dest.x = circulo_x % 640;
dest.y = circulo_y % 480;

SDL_BlitSurface(circulo_key, &ori, screen, &dest);

8
//Realmente lo muestra.
SDL_Flip(screen);
}

¿¿Y esto???
No desesperen, no es tan terrible como parece. Solamente estamos haciendo lo que ya sabemos, pero lo
estamos repitiendo 1000 veces.
Vamos de a poco; primero tenemos que dibujar el fondo, así que seteo las estructuras correspondientes (ori y
dest) para indicar esto que quiero y después bliteo el fondo.
Después vuelvo a setear el ori y el dest, pero esta vez para la lente, y además actualizo los valores de x e y de
donde va a blitear (lente_x++); y bliteo la lente.
Repito pero esta vez para el circulo y bliteo.
Y por último (como ya sabíamos) hago un SDL_Flip().

Resumiendo, fijense que siempre repetimos los mismos pasos y es fácil armar funciones que nos manejen los
distintos objetos de las pantallas.
Enumerando: primero actualizamos en donde queremos que este la imagen a blitear (circulo_x++), segundo
preparamos los 2 SDL_Rect (ori y dest), tercero bliteamos y por último hacemos el SDL_Flip().

Optimización
En el momento de pensar en hacer un juego, uno siempre tiene que tener en mente la optimización; uno como
programador siempre tiene que sacar el mayor provecho a los recursos que brinda la PC.
Afortunadamente para nosotros, SDL viene con varias funciones que nos van a ayudar a hacer nuestro código
un poco mas rápido y mas hablando de imagenes.
Para demostrar esto que digo, vamos a tomar el último ejemplo que hicimos y vamos a modificarlo muy poco
para hacer el Blit mas performante; y después les voy a explicar los pequeños cambios.
El archivo que contiene el código es alpha_per.cpp.

SDL_Surface *temp;
Primero necesitamos tener una superficie intermedia sobre la cual vamos a trabajar.

temp = IMG_Load("imagen.jpg");
imagen = SDL_DisplayFormat(temp);
Entonces en vez de cargar las imagenes directamente, primero la cargamos en la temporal, hacemos los
checkeos y después usamos SDL_DisplayFormat() para transformar la superficie cargada en una superficie
mas óptima.

SDL_FreeSurface(temp);
Una que terminamos, la liberamos.

SDL_SetAlpha(temp, SDL_SRCALPHA | SDL_RLEACCEL, 0);


lente = SDL_DisplayFormatAlpha(temp);
Aquí utilizamos un flag mas 'SDL_RLEACCEL' lo que le decimos es que use el sistema RLE de aceleración
de alpha, el cual va a mejorar el calculo de las transparencias.
Existe una función para cuando queremos optimizar una superficie con Alpha: SDL_DisplayFormatAlpha, es
igual a la anterior; solo que recibe una superficie que tiene alpha.

Resumiendo; hay poco a tener en cuenta, solo acuerdense de usar estos flags de mas y usar estas funciones
intermedias.

9
¿Es el SDL_DisplayFormat mágico?
Casi, el funcionamiento es el siguiente.
Ustedes tienen en su aplicación una superficie la cual es la que maneja la pantalla con un montón de
información sobre como dibujar los pixels en pantalla, y por otro lado tienen superficies cargadas desde
imagenes; rara vez estas 2 superficies son compatibles, entonces en el momento de hacer un Blit, SDL tiene
que transformar los pixels de la imagen cargada para que puedan trabajar con los pixels de la pantalla; esto se
hace cada vez que hacemos un blit. Entonces lo que podemos hacer nosotros es decirle a SDL que transforme
la imagen para que pueda trabajar bien con la pantalla, y que nos devuelva esa superficie; al estar convertida,
en el momento del Blit SDL tiene menos trabajo, lo cual incrementa la velocidad.

En el tintero
Bueno, fue bastante largo, pero las imagenes son un tema largo en cualquier API y no quise cortar por la
mitad una vez que nos íbamos entusiasmando.
Una cosas mas para tener en cuenta antes de despedirnos.

Alpha en vez de Color Key


Nosotros podemos elegir que nuestra imagen tenga como color de fondo transparencia pura, lo cual en vez de
tener que elegir un color key para quitar en el bliteo, solamente tenemos que respetar el canal alpha de la
imagen cargada.
Como la transparencia en este caso es de 100% no hay cálculos de mas ya que estos pixels son tratados de la
misma manera que si fueran un color key. Simplemente no se blitean.

Ordenar
Una cosa muy importante al escribir código es ser ordenados, el código de ejemplo que tienen ahora esta muy
desordenado y hacer eso en un juego real sería una locura.
Lo que hay que tener en cuenta es el código que se repite e ir armando funciones que podamos reutilizar.
Por ejemplo la carga de archivos es un ejemplo típico de una función que vamos a llamar varias veces (quizás
mas adelante les muestro código sobre un manejador de recursos).
Otra cosa para tener en cuenta es sobre los 'elementos' que tenemos en nuestro juego. Aquí yo uso 2 círculos,
y las variables que manejan las posiciones están sueltas en el main (nada mas asqueroso), la idea en esta
situación es armarse un modulo (o clase) que maneje el circulo y ustedes deberían poder llamar a una función
(o método) dibujar que se encargue de manejar las variables de posición.

Despedida
Ahora si, después de varias sugerencias y aclaraciones a tener en cuenta es el momento de terminar este
articulo.
Como siempre cualquier duda, comentario o crítica que tengan las pueden enviar a nesdavid[arroba]gmail
com.
Por si perdieron el link al zip con todos los ejemplos, acá esta de vuelta.
Eso es todo por ahora, así que buenas noches (al menos para mi) y Happy Hacking a todos ustedes.

10

Anda mungkin juga menyukai