06 junio 2008

Programar videojuegos con la librería SDL (7)

CREANDO UN SCROLL VERTICAL

Al igual que nos juegos clásicos de arcade, vamos a crear un scroll vertical hacia abajo para que parezca que estamos avanzando sobre el mar. Realmente vamos a utilizar la misma textura del mar, pero la vamos a dividirla en dos trozos: uno que baja y otro que lo alimenta por arriba:


Mientras hacemos que el fondo baje por la pantalla, arriba se nos va quedando un hueco que tenemos que completar con el mismo fondo. Para hacer esto vamos a implementar en nuestra unidad genérica UJuego.pas un nuevo procedimiento para copiar trozos de imagen de una superficie a otra:

procedure CopiarImagen( Origen, Destino: PSDL_Surface;
x1, y1, x2, y2, iAncho, iAlto: Integer );
var
RO, RD: TSDL_Rect; // rectángulos origen y destino
begin
RO.x := x1;
RO.y := y1;
RO.w := iAncho;
RO.h := iAlto;
RD.x := x2;
RD.y := y2;
RD.w := iAncho;
RD.h := iAlto;
SDL_BlitSurface( Origen, @RO, Pantalla, @RD );
end;

También necesitamos una variable glogal en la unidad UArcade.pas para controlar la posición del scroll:

var
iScroll: Integer;

Y por último modificamos el procedimiento DibujarSprites para conseguir el efecto deseado:

procedure DibujarSprites;
begin
// Parte inferior del scroll
CopiarImagen( Fondo.Superficie, Pantalla, 0, 0, 0, iScroll, 640, 480 - iScroll );

// Parte superior del scroll
CopiarImagen( Fondo.Superficie, Pantalla, 0, 480 - iScroll, 0, 0, 640, iScroll );

// Movemos el fondo hacia abajo
Inc( iScroll );

if iScroll > 480 then
iScroll := 0;

Avion.Dibujar;
ControlarDisparos;
ControlarEnemigos;
end;

Así quedaría al ejecutar el juego:


Si os fijáis en la imagen se puede apreciar el corte de donde empieza y termina la textura del mar verticalmente. Lo ideal sería conseguir una textura simétrica para que no se note dicho escalón. En las páginas sobre texturas 3D que hay por Internet se puede encontrar gran cantidad de material.

ACCEDIENDO A LAS FUENTES PARA ESCRIBIR TEXTO

Renderizar texto en una superficie no es tan fácil como imprimir un mensaje en Windows utilizando el canvas. La librería SDL dispone de dos formas de escribir texto: utilizando una fuente predefinida en un bitmap o utilizar las fuentes reales de Windows mediante archivos TTF.

Yo me he decantado por la segunda opción, ya que aunque es más difícil, sí que nos da más libertad al escribir texto (cambiando la fuente, el estilo, etc.). Para cumplir este objetivo vamos a utilizar una librería asociada a la SDL llamada SDL_TTF. Esta librería se encuentra en un subdirectorio donde hemos instalado la SDL:


Así que ese directorio tenemos que añadirlo a los directorios de búsqueda del proyecto:


También necesitamos bajarnos de Internet la DLL donde está esta librería y guardarla en el directorio de nuestra aplicación:

http://www.libsdl.org/projects/SDL_ttf/release/SDL_ttf-2.0.9-win32.zip

Ese zip lleva estos archivos:

La versión de esa DLL es la 2.0.9, mientras que nuestras cabeceras de SDL del proyecto Delphi son de la versión 1.5, aunque todo funciona a la perfección.

Ahora vamos a hacer una serie de rutinas estándar en nuestra unidad UJuego.pas para poder imprimir texto encima de cualquier superficie. Lo primero es añadir la nueva unidad sdl_ttf en la sección uses:

uses Windows, SysUtils, Dialogs, SDL, sdl_ttf;;

Para poder utilizar las fuentes hay que llamar a los procedimientos TTF_Init y TTF_Quit. Esto lo vamos a encapsular en dos procedimientos:

procedure InicializarFuentes;
begin
TTF_Init;
end;

procedure FinalizarFuentes;
begin
TTF_Quit;
end;

Estos procedimientos hay que llamarlos en bucle principal de programa, en la unidad juego.dpr. Lo llamamos después de inicializar el modo de vídeo:

begin
InicializarSDL( 'Mi primer juego en SDL' );
ModoVideo( 640, 480, 16, True );
InicializarFuentes;
...

y cuando nos salimos del juego:

...
FinalizarFuentes;
FinalizarSDL;
end.


Ahora necesitamos un procedimiento para abrir una fuente de Windows y que se quede en memoria y otro para cerrarla al salir de juego (en la unidad UJuego.pas):

function CrearFuente( sNombre: String; Tamano: Integer ): PTTF_Font;
begin
Result := TTF_OpenFont( PChar( 'c:\windows\fonts\' + sNombre ), Tamano );
end;

procedure EliminarFuente( Fuente: PTTF_Font );
begin
TTF_CloseFont( Fuente );
end;

Como puede verse he utilizado el puntero a una fuente PTTF_Font para crearla dando además la ruta del directorio de Windows. Aunque para crear un juego profesional independiente habría que copiar la fuente que vamos a utilizar en nuestro directorio local y abrirla desde ahí, no vaya a ser que el usuario no tenga esa fuente instalada en Windows.

El siguiente procedimiento que vamos a crear va a dibujar el texto en la superficie que le pasemos como parámetro:

procedure EscribirTexto( Superficie: PSDL_Surface; x, y: Integer; sTexto: String;
Fuente: PTTF_Font; Color: TSDL_Color );
var
Texto: PSDL_Surface;
begin
Texto := TTF_RenderText_Solid( Fuente, PChar( sTexto ), Color );
CopiarImagen( Texto, Superficie, 0, 0, x, y, Texto.w, Texto.h );
SDL_FreeSurface( Texto );
end;

La función TTF_RenderText_Solid escribe el texto en una superficie, pero hay que llevar cuidado, ya que lo que hace realmente es crear una nueva superficie y escribir texto en ella. Luego hay que acordarse de liberarla de memoria llamando al procedimiento SDL_FreeSurface.

DIBUJANDO LOS MARCADORES DE PUNTUACIÓN

Ahora que ya tenemos todo lo que necesitamos para escribir texto en una superficie vamos a crear la fuente en nuestra unidad UArcade.pas. Lo primero es vincular también la unidad sdl_ttf al principio de la unidad:

uses SysUtils, UJuego, SDL, sdl_ttf;

Después creamos las variables para almacenar la fuente, el color de la misma y la puntuación que llevamos acumulada en el juego:

var
...
Verdana: PTTF_Font;
Blanco: TSDL_Color = ( r: $FF; g: $FF; b: $FF; unused: 0 );
rPuntuacion: Real;

Los colores en la librería SDL se almacenan en la estructura de datos TSDL_Color especificando los colores rojo (red -> r), verde (green -> r ) y azul (blue -> b). En este caso, como voy a escribir la puntuación en color blanco, he puesto cada valor a $FF.

Lo siguiente es ampliar el procedimiento CargarSprites para que cargue la fuente verdana:

procedure CargarSprites;
begin
...
Verdana := CrearFuente( 'verdana.ttf', 20 );
end;

Y también nos encargamos de liberarla en el procedimiento DestruirSprites:

procedure DestruirSprites;
begin
EliminarFuente( Verdana );
...
end;

Para dibujar el marcador de puntuación en pantalla encima de todo lo demás, tenemos que hacerlo después de que se dibujen todos los sprites (fondo, aviones, etc.). Por ello vamos a dibujar el marcador de puntuación al final del procedimiento DibujarSprites:

procedure DibujarSprites;
begin
...
EscribirTexto( Pantalla, 450, 0, 'Puntuación: ' +
FormatFloat( '0000', rPuntuacion ), Verdana, Blanco );
end;

Por último, tenemos que modificar el procedimiento ControlarDisparos para que incremente la puntuación en 1 cuando un disparo alcanza al enemigo:

procedure ControlarDisparos;
var
i, j: Integer;
begin
for i := 1 to 10 do
if Disparos[i].bActivo then
begin
Disparo.x := Disparos[i].x;
Disparo.y := Disparos[i].y;
Disparo.Dibujar;

Dec( Disparos[i].y, 20 );

if Disparos[i].y < -20 then
Disparos[i].bActivo := False;

for j := 1 to 10 do
if Enemigos[j].bActivo then
if ( Disparos[i].x + 16 >= Enemigos[j].x ) and
( Disparos[i].x + 16 <= Enemigos[j].x + 95 ) and
( Disparos[i].y + 9 >= Enemigos[j].y ) and
( Disparos[i].y + 9 <= Enemigos[j].y + 73 ) then
begin
Enemigos[j].bActivo := False;
Enemigos[j].bExplotando := True;
Enemigos[j].iTmpExplosion := SDL_GetTicks;
Disparos[i].bActivo := False;
rPuntuacion := rPuntuacion + 1;
end;
end;
end;


Y este es el resultado final al ejecutar el juego:


En este artículo tenía previsto cómo reproducir sonido y música utilizando la librería SDL, pero como es bastante extenso lo vamos a dejar para la semana que viene.

En el próximo artículo veremos también como cargar los gráficos utilizando los formatos JPG y PNG (los más avanzados) y veremos un ejemplo que como crear un juego de plataformas mediante piezas (tiles).

Aquí tenéis todo el proyecto en RapidShare, Megaupload y Hyperupload:

https://mega.nz/file/4JgUwYJC#afjcV-Jf4os5yDRJ9zl3l3Cbj5Bjt-TCCBb5t6P94gs

Pruebas realizadas en Delphi 7.

Publicidad