25 abril 2008

Programar videojuegos con la librería SDL (1)

Hace mucho tiempo leí en una página web que uno no se hace programador profesional hasta que ha programado uno o más videojuegos (o por lo menos lo ha intentado). Por mi experiencia personal creo que tienen razón. En un videojuego no hay trucos de lenguajes, librerías o componentes. Todo hay que hacerlo a pelo. Nuestras funciones y procedimientos se ejecutan de 25 a 30 veces por segundo y ante cualquier error nuestro (bucles cerrados, objetos creados y no liberados, etc.) nos podemos cargar la pila de nuestra aplicación o el heap de datos mejor que el virus más maléfico del mercado.

Peor era en mis tiempos, cuando programaba en ensamblador con mi MSX que tenía un procesador Z80 de 8 bits. Cuando se colgaba el juego había que resetear la máquina, rebobinar la cinta de casete y esperar dos minutos a que se cargue el ensamblador y el debugger. Eso si que eran buenos tiempos.

Aunque no os preocupéis, la programación de videojuegos en PC no es tan traumática. Vamos a ver paso a paso como crear un nuevo proyecto en Delphi y adaptarlo para añadirle la librería SDL.

SDL (Simple DirecMedia Layer) es una librería multiplataforma escrita en el lenguaje de programación C/C++ y disponible para todos los sistemas operativos (Windows, Linux, BeOS, NetBSD, etc.). Permite controlar todo lo referente al video 2D y 3D con OpenGL, reproduce archivos multimedia y controla el teclado, ratón o joystick.

DONDE CONSEGUIR LA LIBRERÍA SDL PARA DELPHI

Como todos sabéis, existe la página Project JEDI Portal encargada de traducir librerías y utilidades en C/C++ al Pascal/Delphi. Dentro de esa inmensa cantidad de librerías se encuentra el proyecto JEDI-SDL que es el que nos interesa:

Es en esta dirección donde está lo que nos interesa:

http://sourceforge.net/project/showfiles.php?group_id=43805

El archivo es un zip de 5.5 MB. Lo que tenemos que hacer es crear una carpeta donde alojar la librería de tal modo que varios proyectos puedan tirar de ella, por ejemplo:

D:\desarrollo\librerias\sdl\

Ahí es donde tenemos que descomprimir el archivo JEDI-SDLv1.0.zip que nos hemos descargado. Nos va a extraer todas estas carpetas:


Una vez tenemos nuestra librería instalada ya podemos ponernos manos a la obra.

CREANDO UN JUEGO DELPHI CON LA LIBRERÍA SDL

Los pasos para crear un nuevo juego serían los siguientes:

1. Creamos un nuevo proyecto y lo guardamos en la carpeta donde vamos a crear el juego, por ejemplo:

D:\desarrollo\delphi\juego\

2. Eliminamos el formulario principal Form1 (si, habéis leído bien, nos vamos a cargar todos los formularios del proyecto. Hay que olvidarse de formularios y componentes).

3. Guardamos el proyecto con el nombre juego.dpr

4. Abrimos la única unidad que tiene el proyecto: CTRL + F12 y Juego.

Para que el código fuente quede lo más limpio posible sólo vamos a insertar en la unidad juego.dpr el núcleo central del programa. Todo lo referente a las rutinas de inicialización y manejo de la SDL la vamos a añadir en otra unidad.

CREANDO UNA UNIDAD PARA RUTINAS GENÉRICAS

Vamos a crear la unidad UJuego.pas la cual va a contener todas las rutinas genéricas que puede tener un juego. Cuando se diseña un programa siempre hay que pensar en reutilizar el código para un futuro.

Lo primero que hay que añadir en esta unidad es una referencia a la librería SDL. Para ello primero vamos a vincular al proyecto las unidades SDL que necesitamos. En el menú superior de Delphi seleccionamos Proyect -> Options y en la pestaña Directories/Conditionals vamos a añadir al campo Search Path la carpeta:

D:\Desarrollo\Librerias\sdl\SDL\Pas\

De este modo ya podemos vincular la librería SDL a nuestra unidad:

También he añadido las unidades Windows, SysUtils y Dialogs porque las vamos a necesitar para llamar a algunas funciones y procedimientos estándar.

INICIALIZAR LA LIBRERÍA SDL

El primer procedimiento que vamos a crear se va a llamar InicializarSDL, el cual va a encargarse de inicializar el modo de video en 2D para poder dibujar:

procedure InicializarSDL;
begin
// Inicializamos la librería SDL
if SDL_Init( SDL_INIT_VIDEO or SDL_DOUBLEBUF ) < 0 then
begin
ShowMessage( 'Error al inicializar la librería SDL.' );
SDL_Quit;
bSalir := True;
Exit;
end;

bSalir := False;
end;

bSalir es una variable global que vamos a declarar también en la unidad UJuego.pas para controlar si hay que salir del juego inmediatamente:

Var
bSalir: Boolean;

La función SDL_Init se encarga inicializar la librería SDL, controlando todos los dispositivos que vamos a inicializar: video, sonido, ratón, joysticks, etc. Tiene los siguientes parámetros:

function SDL_Init( flags : UInt32 ) : Integer;

donde flags puede contener a la vez todos estos valores:

SDL_INIT_TIMER: Prepara el temporizador de la librería SDL.
SDL_INIT_AUDIO: Prepara la tarjeta de sonido.
SDL_INIT_VIDEO: Prepara el modo de video.
SDL_INIT_CDROM: Prepara el CD-ROM.
SDL_INIT_JOYSTICK: Inicializa el joystick.
SDL_INIT_EVERYTHING: Inicializa todos lo que hemos visto anteriormente.

También vamos a crear otro procedimiento para finalizar la ejecución de la librería SDL:

procedure FinalizarSDL;
begin
SDL_Quit;
end;

Este procedimiento es importante para que la librería SDL suelte los dispositivos que tiene cogidos en Windows (teclado, ratón, tarjeta de sonido, etc.).

CAMBIAR EL MODO DE VÍDEO EN SDL

El siguiente procedimiento que vamos a crear se va a encargar de cambiar el modo de video:

procedure ModoVideo( iAncho, iAlto, iProfundidadColor: Integer; bModoVentana: Boolean );
begin
// Pasamos a modo de video de la ventana especificada
if bModoVentana then
Pantalla := SDL_SetVideoMode( iAncho, iAlto, iProfundidadColor, SDL_HWSURFACE )
else
Pantalla := SDL_SetVideoMode( iAncho, iAlto, iProfundidadColor, SDL_HWSURFACE
or SDL_FULLSCREEN );

if Pantalla = nil then
begin
ShowMessage( 'Error al cambiar de modo de video.' );
SDL_Quit;
Exit;
end;
end;

Los parámetros son los siguientes:

iAncho -> Resolución horizontal en pixels (320, 640, 800, 1024)
iAlto -> Resolución vertical en pixels (200, 480, 600, 768)
iProfundidadColor -> número de bits por píxel (8, 16, 32)
bModoVentana -> True: en ventana False: Pantalla completa

Cuando se desarrolla un juego por primera vez lo mejor es hacerlo en modo ventana, porque si se produce un error va a costar mucho volver al escritorio de Windows e incluso puede dejarnos el modo de video en una resolución inferior a la que deseamos (desordenando todos los iconos del escritorio). Cuando el juego esté terminado y funcione bien entonces activamos el modo a pantalla completa.

La resolución de los modos de video estándar son los siguientes:

320 x 200
640 x 480
800 x 600
1024 x 768

La profundidad de color se refiere al número de bits por pixel pudiendo seleccionar los valores: 8, 16 o 32. Estos son los valores estándar en la creación de videojuegos, pero hay muchos modos de vídeo más según la tarjeta (nVidia, ATI, Matrox, Intel, etc.). Si vamos a hacer un juego normal que no consuma muchos recursos tenemos dos opciones:

ModoVideo( 640, 480, 16, False );
ModoVideo( 800, 600, 16, False );

Si la tarjeta de video es buena (una aceleradora 3D) los valores serían:

ModoVideo( 640, 480, 32, False );
ModoVideo( 800, 600, 32, False );

Estas son las resoluciones que más se utilizan en el desarrollo de juegos Indie (los juegos Indie son los realizados por desarrolladores independientes pero que suelen tener buena calidad y se venden a bajo precio: de 10 a 20 €).

La variable Pantalla que hemos utilizado en el procedimiento ModoVideo la vamos a declarar también como global (al lado de bSalir):

var
bSalir: Boolean;
Pantalla: PSDL_Surface;

PSDL_Surface es el puntero a un objeto SDL_Surface. Para poder dibujar en SDL se necesita una superficie, la cual puede estar en la memoria de video (lo recomendable) o en la memoria RAM (sólo para guardar sorites y texturas cuando son muchas).

El puntero Pantalla que yo he creado representa directamente la pantalla de video donde vamos a dibujar. Para crear esta pantalla he utilizado la siguiente función de la librería SDL:

function SDL_SetVideoMode(width, height, bpp: Integer; flags: UInt32): PSDL_Surface;

Los parámetros son los siguientes:

Width -> Ancho de la superficie
Height -> Altura de la superficie
Bpp -> Número de bits por píxel
Flags -> Que tipo de superficie vamos a crear

Los tipos de superficie que admite el parámetro flags son los siguientes:

SDL_SWSURFACE: la superficie estará en la memoria RAM
SDL_HWSURFACE: la superficie estará en la memoria de video (recomendado)

También se puede combinar con los siguientes valores:

SDL_ANYFORMAT: admite cualquier resolución o profundidad de color
SDL_HWPALETTE: tendrá una paleta de color exclusiva (sólo se utiliza en modos de 8 bits, esto ya está obsoleto).
SDL_DOUBLEBUF: tiene doble buffer (ya lo explicaré más adelante)
SDL_FULLSCREEN: la superficie estará a pantalla completa
SDL_OPENGL: va a utilizar la libería OpenGL para dibujar polígonos en 3D
SDL_RESIZABLE: el modo de video puede ser redimensionado
SDL_NOFRAME: la ventana no tendrá ni título ni bordes

En la siguiente parte de este artículo crearemos el bucle principal de la aplicación y veremos como cargar las figuras gráficas (sprites) para darles movimiento.

Pruebas realizadas en Delphi 7.

Publicidad