best counter
GNU/Linux. MuyLinux

Programación de videojuegos con SDL – Parte I: Introducción

16/08/2010| por | 23 comentarios

Hola chicos, ¿Cómo estáis? Ya sé que llevo mucho tiempo sin escribir nada en MuyLinux, pero era porque me estaba preparando este tutorial que os traigo hoy y que espero que os guste, ya que últimamente se venía hablando sobre el tema que trataremos.

SDL 1 Programación de videojuegos con SDL – Parte I: Introducción

Pero atención, porque este no va a ser un tutorial cualquiera, porque os voy a enseñar lo básico para que empecéis a programar videojuegos en Linux.

Conociendo a SDL

Para empezar con buen pie vamos a dejar claras un par de cosas. En este tutorial  voy a presuponer que tenéis al menos un dominio básico del lenguaje C, no estoy hablando de estructuras complejas ni operaciones con punteros de nada de eso. Simplemente saber usar funciones, bucles while, estructuras condicionales como los if/else, etc.

Otra cosa que quiero aclarar, y que seguramente habeis adivinado, es la biblioteca que usaremos para las operaciones gráficas, que será nada más y nada menos que la famosa Simple DirectMedia Layer, a partir de aquí SDL.

Me he decidido por SDL por varios motivos. Primero porque está escrito en C, así que no nos hará falta instalar bindings para otros lenguajes como pygame, simplemente necesitamos instalar los paquetes de desarrollo de SDL que podréis encontar en vuestro gestor de paquetes preferido. Otro motivo es que, al estar orientado solo a gráficos 2D, será mucho más fácil aprender desde el principio, en lugar de liarnos con cosas en 3D usando OpenGL, mucho más complejo.

Es importante mencionar que, si bien SDL nos ofrece una completa API para acceder no solo a gráficos, sino al sistema de sonido, a los sistemas de entrada (teclado, ratón, incluso joysticks), a la interacción con CDs e inclusive al juego multijugador en línea, varios de estos subsistemas son algo espartanos, por lo que existen módulos auxiliares que potencian con mucho al SDL básico, tales como SDL_Image, SDL_Mixer, etc.

En nuestro tutorial no haremos uso de estos módulos porque el fin del mismo es ser una introducción al desarrollo de juegos 2D bastante simples. Para el que quiera información en detalle sobre SDL o sus módulos puede dirigirse a la web oficial de SDL (en inglés).

Como nota, os aviso de que al final del artículo tenéis un enlace para descagaros el código fuente comentado línea a línea junto con todas las imágenes empleadas. También he de decir que, para todos los que no estéis muy versados en C, en la segunda parte del tutorial, junto con el código fuente también incluiré una versión de lo que haremos hoy aquí pero hecha en Python (también comentado lo más posible).

Poniéndonos manos a la obra

Una vez hechas las presentaciones espero que ya tengáis instalados los archivos de desarrollo de SDL y tengáis abierto vuestro editor de textos favorito porque esto empieza ya, amigos. Vamos a ir todo lo despacito que podamos así que no os perdáis.

Primero vamos con lo básico, avisarle al preprocesador qué bibliotecas ha de incluir a la hora de compilar el archivo, así que empezamos escribiendo lo siguiente:

#include <stdio.h>
#include <stdlib.h>
#include <SDL/SDL.h>

Las dos primeras bibliotecas no son ni más ni menos que las bibliotecas estándar de C. La tercera me parece que es obvio a quién llama. Ahora vamos a definir unas cuantas constantes para nuestro juego. Nada complicado, simplemente:

#define WIDTH 420
#define HEIGHT 360
#define BPP 24

Estas líneas le indican al preprocesador que, en todo lugar donde encuentre las palabras WIDTH, HEIGHT o BPP, se sustituya la misma por el valor que hemos definido. Las dos primeras definen el ancho y alto de la ventana en la que mostraremos los gráficos, mientras que la tercera línea se define el número de bits por pixel, comúnmente conocido como profundidad de color. Lo siguiente que debemos hacer será crear todas las variables y todos los objetos que necesitará nuestro videojuego.

SDL_Surface *image, *screen;
SDL_Rect dest;
SDL_Event event;
int done = 0;
Uint8 *keys;

Por ahora nada demasiado sorprendente: creamos dos superficies, una contendrá la imagen de nuestro jugador y la otra contendrá todos los elementos, es decir, es la pantalla principal. También creamos un elemento Rect que simplemente es un rectángulo, y que nos servirá para definir la posición en pantalla del jugador. Creamos un elemento Event que nos ayudará a saber si se sale o no del juego, y una variable llamada done que será la que marque la salida de la aplicación. El elemento keys nos servirá para monitorizar el estado de las teclas de nuestro teclado.

Acto seguido preparamos una estructura muy muy sencilla que nos facilitará darle unas coordenadas a nuestra nave. Dicha estructura quedaría tal que así:

struct nave{
    int x,y;
} minave;

Con esto hemos terminado de hacer los preparativos para nuestro juego. A partir de ahora todo el código deberá estar dentro de la función main del programa. Lo primero que pondremos será algo así:

atexit(SDL_Quit);

if(SDL_Init(SDL_INIT_VIDEO) < 0)
{
    printf("No se ha podido iniciar SDL: %s\n", SDL_GetError());
    exit(1);
}

La primera sentencia es fácil de entender, indica que ha de cerrarse SDL al salir de la aplicación. La función usada en la sentencia if ya es más especial. La función SDL_Init es la encargada de inicializar los subsistemas de SDL y admite como parámetros varias opciones como por ejemplo: SDL_INIT_VIDEO, SDL_INIT_AUDIO, SDL_INIT_CDROM, iniciando cada una de ellas el subsistema gráfico, de sonido o del CD-ROM respectivamente. En caso de usar varias a la vez, irán separadas por una barra vertical. Admite varias más pero no quiero pararme en eso ahora. Este bloque informa al usuario de si ha ocurrido algún error iniciando SDL.

Una vez que tenemos inicializado el subsistema gráfico, podemos empezar a poner en marcha la que será la pantalla principal. Vamos a activar la variable screen que hemos creado antes como nuestra pantalla por defecto, lo cual es tan simple como

screen = SDL_SetVideoMode(WIDTH, HEIGHT, BPP, SDL_HWSURFACE);

if(screen == NULL)
{
    printf("No se ha podido establecer el modo de vídeo: %s\n", SDL_GetError());
    exit(1);
}

La función SDL_SetVideoMode() define una superficie como la pantalla principal. Tiene cuatro parámetros que son el ancho, el alto, la profundidad de color, y los flags. En nuestro caso hemos usado el flag SDL_HWSURFACE para que SDL active la aceleración por hardware de estar disponible. Podeis consultar qué flags admite en la web oficial de SDL. Como en el caso anterior, avisamos al usuario de si ha ocurrido algún error al establecer el modo de vídeo.

Jugando con imágenes

Vamos ahora a empezar a ver cosas tangibles en nuestra pantalla. Vamos a cargar la imagen de nuestro jugador y para eso recurriremos a la función SDL_LoadBMP() que toma como único parámetro la ruta del archivo en formato de cadena:

image = SDL_LoadBMP("img/minave.bmp");
if(image == NULL)
{
    printf("No se ha podido cargar la imagen: %s\n", SDL_GetError());
    exit(1);
}

Lo que vamos a hacer ahora es darle unas coordenadas iniciales a nuestro jugador, le asignaremos una posición en pantalla usando la variable dest y acto seguido lo mostraremos en pantalla:

minave.x = 50;
minave.y = 10;

dest.x = minave.x;
dest.y = minave.y;
dest.w = image -> w;
dest.h = image -> h;

SDL_BlitSurface(image, NULL, screen, &dest);
SDL_Flip(screen);
SDL_FreeSurface(image);

Las dos primeras líneas establecen las coordenadas iniciales de la estructura de nuestra nave. Las 4 siguientes definen las coordenadas y el tamaño de la zona en pantalla que ocupará nuestra imagen. Con la función SDL_BlitSurface() copiamos el contenido de la variable image sobre la superficie screen, es decir, la mostramos en pantalla. Los parámetros de esta función son: la superficie a copiar, la zona de dicha superficie que se desea copiar, la superficie de destino, y la zona de dicha superficie donde se copiarán los datos.

imagen2 Programación de videojuegos con SDL – Parte I: Introducción

La función SDL_Flip() actualiza una superficie. En nuestro caso actualizará toda la pantalla. Seguidamente, liberamos los recursos ocupados por la imagen haciendo uso de la función SDL_FreeSurface(). Vamos ahora a escribir el loop principal del juego:

while(done == 0)
{
    while(SDL_PollEvent(&event))
    {
        if(event.type == SDL_KEYDOWN) {done = 1;}
    }
}

Si compilamos nuestra aplicación como la tenemos hasta ahora, obtendríamos algo similar a lo que vemos arriba, una bonita nave espacial con un feo contorno rojo.

imagen3 Programación de videojuegos con SDL – Parte I: Introducción

Para los impacientes, podéis compilar el archivo hasta este punto con el comando

gcc -o navecitas navecitas.c -lSDL

Y lo ejecutáis simplemente escribiendo

./navecitas

Pero ese contorno rojo queda muy rancio y además poco estético ¿Verdad? No hay problema. SDL nos ofrece una función para utilizar un canal de color alfa que se comportará como si fuese transparente, eliminando ese feo contorno. Dicha función se llama SDL_SetColorKey y la usaremos de la siguiente manera, colocándola justo después de la carga y verificación de la imágen

SDL_SetColorKey(image, SDL_SRCCOLORKEY | SDL_RLEACCEL, SDL_MapRGB(image -> format, 255, 0, 0));

Aviso para navegantes: No os preocupéis sino entendéis el porqué de algunas funciones o parámetros que pongo aquí, pues está todo comentado y explicado en el código fuente, de manera que pretendo ahorrarme un doble esfuerzo, ya que tampoco puedo alargar mucho el post. Por ahora quedaos con que esta función colocará como color transparente en nuestra imagen el color rojo. Si compilamos de nuevo nuestro archivo con la orden anterior podremos ver algo parecido a lo siguiente:

imagen4 Programación de videojuegos con SDL – Parte I: Introducción

Ahora tiene mucha mejor pinta. Para ir finalizando voy a colocar el bloque de código que se encargará de los controles de la nave. Tened en cuenta que a partir de este punto varias cosas cambian de sitio en el código fuente, y no pretendo pararme mucho. Os recomiendo revisar el código fuente y lo vereis mucho mejor.

Controlando la nave

Algún lector espabilado se habrá dado cuenta de que aún no hemos tocado la variable keys creada al comienzo. Pues a eso vamos en esta sección. Usaremos la función de SDL llamada SDL_GetKeyState() para monitorizar cuales teclas están o no pulsadas en el momento de ejecutarse dicha función. Posteriormente usaremos varias estructuras if para que, en caso de pulsarse alguna tecla de dirección, la nave se mueva en dicha dirección. El código nos quedaría de esta manera

keys = SDL_GetKeyState(NULL);

if(keys[SDLK_UP] && minave.y > 0) {minave.y = minave.y - (5);}
if(keys[SDLK_DOWN] && minave.y < HEIGHT) {minave.y = minave.y + (5);}
if(keys[SDLK_LEFT] && minave.x > 0) {minave.x = minave.x - (5);}
if(keys[SDLK_RIGHT] && minave.x < WIDTH) {minave.x = minave.x + (5);}

Y con esto y un par de instrucciones extra en el loop principal de nuestro juego, tenemos una bonita nave que podemos controlar con nuestro teclado. Sí, no es el Crysis, pero Roma no se construyó en un solo día ;). Si habeis llegado hasta aquí, os doy las gracias por tragaros todo este tocho. Espero que os haya gustado y estad atentos porque el miércoles publicaremos la segunda parte del tutorial (y el viernes la tercera y última), en el que escribriremos una pequeña biblioteca sobre la que nos apoyaremos para seguir avanzando. Recordad descargaros el código fuente junto con la imagen usada desde este enlace y seguid practicando.

Contenido del curso

Programación de videojuegos con SDL – Parte I: Introducción

Programación de videojuegos con SDL – Parte II: bibliotecas C++

Programación de videojuegos con SDL – Parte III: a jugar

Related posts:

  1. Programación de videojuegos con SDL – Parte III: a jugar
  2. Programación de videojuegos con SDL – Parte II: bibliotecas C++
  3. Introducción a la programación de plasmoides
  4. ¿Cuál es la situación del mercado de los videojuegos en Mac OS X y en Linux?
  5. Curso de Lyx I: Introducción

Hay 23 comentarios

  1. 1
    aetsu dice:

    Interesante la SDL, yo la probé con python (pygame) y se podían hacer cosas bastante bonitas.
    Espero la segunda parte del tutorial ;)

  2. 2
    Julio_Sao dice:

    Muy interesante este post si señor a ver si me animo a tocar el tema jejeje hace mucho programé en Div Games Studio y aun tengo algo de mono por el tema jejeje

  3. 3
    JRB dice:

    Hola.

    Me encanta este tipo de artículos.

    Os acordáis que antes se hacía broma porqué sólo se hablaba de Ubuntu? Últimamente se tocan muchas más, como Debian, Red Hat, Mandriva… Y también *BSD!

    Pero, además, ahora también hay artículos de programación!!

    Felicidades a Garolard y al resto de los colaboradores. Espero que la colaboración con el blog os permita encontrar un trabajo relacionado con esto, porque con la currada que os estáis pegando, os lo merecéis.

    Por favor, seguid así!

    Un saludo!

    • 5
      Jortecus dice:

      y ahora tampoco enlazan articulos de aca con los de muycomputer [como el de la muerte de opensolaris], aunque nunca vi por que las quejas

  4. 4
    Sua dice:

    Personalmente me encanta el articulo, no soy muy diestro en programacion, pero probare a ver que sale xd.

  5. 6

    Está muy bien el tutorial, muy directo. Si alguien quiere profundizar le recomiendo el wiki de libSDL que creó un compañero como Proyecto Final de Carrera en Ingeniería Técnica en Informática de Sistemas en la Universidad de Cádiz:

    http://softwarelibre.uca.es/wikijuegos/

    Espero que sea útil, está genial y trata en detalle cada apartado de la SDL.

    Saludos.

    • 8
      Garolard dice:

      Excelente aporte David, he estado mirando por encima y parece detalladisimo. Sin duda tendré que echarle un ojo en cuanto tenga algo más de tiempo. Gracias!

  6. 7
    Dafero dice:

    Buen post! Muchas gracias!!!

  7. 9
    Gobi dice:

    Muy buena entrada, me puse a jugar un poco con Python y SDL y es genial. ¿Qué tan dificil puede ser OpenGL?

    • 10
      Garolard dice:

      Pues depende Gobi. Ten en cuenta que OpenGL trabaja en 3D, es decir, tienes un eje adicional, tienes que tener en cuenta la profundidad. Además OpenGL ofrece acceso a primitvas un tanto limitadas, como puntos, rectas o polígonos sencillos, así que para realizar gráficos complejos han de combinarse dichas primitivas.

      En fin, partiendo desde cero OpenGL puede ser complicado, pero desde luego es mucho más espectacular gráficamente ya que como te he dicho trabaja en 3D.

      De todas formas (y se me olvidó comentar en el tutorial) es que SDL puede combinarse con OpenGL y trabajar así en un contexto 3D. La verdad es que es un mundillo super interesante.

  8. 11
    Jesús dice:

    Muy interesante SDL, lo único que no me gusta es que al ejecutar el ejemplo de este post mi procesador se pone a un 50%, y por lo que he leído por Internet es un “problema” de la librería. Me parece excesivo un 50% para un juego/ejemplo tan poco complejo.
    Igualmente felicitar y animar al autor.

    • 12
      fidojones dice:

      Arriba en los defines:

      #define FPS_WANTED 60 // Cambiar a gusto del consumidor

      Debajo del ultimo while poner:

      SDL_Delay(1000/FPS_WANTED);

      Y asi se reduce los frames por segundo y consumo de CPU.

      • 13
        Jesús dice:

        Gracias por el aporte! He conseguido bajar a un 35% el uso de la CPU, pero aun así me parece excesivo.

        • 14
          Garolard dice:

          Una pregunta Jesús, el 35% es el uso total de CPU o solo el del proceso del ejemplo? Lo digo porque si es el total de uso, yo lo vería hasta normal pues hay más procesos corriendo a parte del ejemplo.

          Ahora mismo estoy ejecutando el mismo ejemplo pero con 3 naves en movimiento y una disparando y el proceso oscila entre un 15 y un 25% de CPU en momentos puntuales con un consumo constante de ram de 1,8 MiB.

          • 18
            Jesús dice:

            Es el uso del proceso del ejemplo y usa 1,6MB de RAM. Mi sistema en estado _normal_ usa un 2% y cuando ejecuto el ejemplo se dispara el uso de CPU.

          • 23
            Carlos dice:

            He trabajando bastante con SDL y no he notado eso de que el procesador se sature, ahora es muy común que en los tutoriales o libros donde enseñan SDL pongan un bucle while que se ejecuta hasta que pasa el tiempo que uno quiere es lógico que el procesador va a llegar al 80 o mas de consumo por que le estas pidiendo que haga una tarea repetitiva, la idea es usar el delay que hace que el procesador deje de atender un proceso por determinado tiempo, asi como decia otra respuesta el delay se calcula dependiendo de los FPS que se quieren, un sistema mas sofisticado seria contar cuanto tiempo tomo procesar un cuadro del juego y se hace el delay por el tiempo que falto para completar el tiempo establecido por los FPS. como ejemplo pongo un motor que escribí, al correr el test se realizan muchos procesos, gestion de actores, del mapa, etc. además esta reproduciendo una canción y el juego corre a 60 FPS y el proceso del juego solo ocupa el 30 % del procesador, ese consumo depende de cuantas cosas haya en la escena, pero se van eliminando cuando salen de cuadro lo que mantiene el consumo bajo tanto de prosesador como de memoria.

  9. 15
    Nitro dice:

    He probado tu codigo fuente en Windows en Dev-C++ con la libreria SDL 1.2.14…. y funciona! :D
    Lo del consumo al 50 % de CPU era verdad, pero he hecho los cambios (FPS_WANTED) y me ha bajado hasta el 3-5%

    Espero con ansia la próxima parte del tutorial ^^ y mil gracias!!

    • 16
      Garolard dice:

      Me alegro de que te haya funcionado Nitro. La verdad es que hay dos puntos que afectan al rendimiento en este ejemplo:

      1- El refresco de la pantalla es completo, es decir, al pintar la pantalla de negro, se pinta TODA la pantalla, cuando podría pintarse únicamente la zona ocupada por la nave, lo que ahorraría recursos.

      y

      2- Como ha comentado fidojones, el control de los frames por segundo que en la primera parte no implementé por cuestiones de longitud del post, pero estad tranquilos que para la versión “completa” del tutorial se usará un simple sistema de control de fps para obtener mejor rendimiento.

  10. 17
    Ivanovichenco dice:

    Compa, te seguiré con los tutos, animo y gracias por tu tiempo

  11. 19
    redtitle dice:

    Muy interesante y agradecido, en mi humilde opinión creo que este es el camino acertado, hacer tutoriales y guias técnicas, en muylinux debería haber mas guias así y menos prensa amarilla.

    Muy bien por muylinux!

    • 20
      redtitle dice:

      y por el currazo de Garolard por supuesto!!

  12. 21
    Javi dice:

    Gracias, muy buen tutorial. Lo único que le falta es explicar al principio como se instala SDL.
    Saludos

  13. 22
    David V. dice:

    Ola Garolard! estoy haciendo un trabajo de recerca sobre inteligencia artificial y no tengo ni idea de programar ni nada parecido, pero esa es mi intención. (voy a segundo de bachillerato)

    ¿cual es el editor de textos? ¿como instalarlo o abrirlo en caso de que lo tenga?

    Siento mi ignorancia acerca del tema, pero porfavor intenta ayudarme.

Escribe tu comentario