20 junio 2008

Programar videojuegos con la librería SDL (9)

CREANDO EL JUEGO DE PLATAFORMAS

El juego de plataformas que vamos a crear va a estar en una ventana de 640 x 480 con el siguiente fondo:


Para dibujar las plataformas voy a dividir toda la pantalla en piezas (tiles) de 40 x 40, lo que nos da 16 piezas horizontales y 12 verticales:


El juego va a constar de los siguientes piezas:


Vamos a tener dos clases de suelo (roca y madera), unas escaleras y las joyas que tiene que recoger el heroe. La primera pieza (la que está en negro) es el fondo transparente que por ahora no voy a utilizar.

CREANDO UN NUEVO PROYECTO

Vamos a crear un nuevo proyecto y aprovecharemos las unidades del proyecto anterior siguiendo estos pasos:

1. Creamos una nueva carpeta y un nuevo proyecto en Delphi.

2. Eliminamos el formulario principal de la aplicación (Unit1.pas).

3. Guardamos el proyecto con el nombre Juego.dpr.

4. Cerramos el proyecto.

5. Copiamos a este directorio las siguientes unidades del juego anterior: Juego.pdr, UJuego.pas.

6. Volvemos a abrir el proyecto y añadimos la unidad UJuego.pas.

7. Añadimos a la ruta de búsqueda del proyecto los siguientes directorios:

D:\Desarrollo\Librerias\sdl\SDL\Pas
D:\Desarrollo\Librerias\sdl\SDL_ttf\Pas
D:\Desarrollo\Librerias\sdl\SDL_Mixer\Pas
D:\Desarrollo\Librerias\sdl\smpeg\Pas
D:\Desarrollo\Librerias\sdl\SDL_Image\Pas

Como vimos en artículos anteriores, estos directorios forman parte de la librería SDL. Por último, necesitamos copiar las librerías dinámicas DLL del directorio anterior:


Con esto ya tenemos nuestra librería estándar para crear un nuevo juego.

CREANDO UNA NUEVA UNIDAD PARA EL JUEGO

Vamos a crear una nueva unidad llamada UPlataformas.pas. A esta unidad le vamos a vincular la ruta de las librerías SDL y de nuestro juego:

uses
SysUtils, Classes, UJuego, SDL, sdl_ttf;

En la unidad del proyecto (Juego.pdr) eliminamos la unidad UArcade.pas del juego anterior:


Ahora vamos a definir en la unidad UPlataformas.pas unas unidades globales para guardar los sprites del fondo y las piezas:

var
Fondo, Temporal, Piezas: TSprite;

He creado también una pantalla temporal donde vamos a mezclar el fondo con las piezas. Ahora implementamos el procedimiento encargado de cargar los sprites:

procedure CargarSprites;
begin
Fondo := TSprite.Create;
Fondo.CargarSuperficie( ExtractFilePath( ParamStr( 0 ) ) + 'fondo.jpg' );

Temporal := TSprite.Create;
Temporal.CargarSuperficie( ExtractFilePath( ParamStr( 0 ) ) + 'fondo.jpg' );

Piezas := TSprite.Create;
Piezas.CargarSuperficie( ExtractFilePath( ParamStr( 0 ) ) + 'piezas.png' );
Piezas.bSubsprites := True;
Piezas.iAnchoSub := 40;
Piezas.iAltoSub := 40;
end;

El fondo y la pantalla temporal son sprites normales, pero las piezas las hemos definido con subsprites de 40 x 40 cada uno.

También necesitamos el procedimiento encargado de destruir los sprites cuando termina el juego:

procedure DestruirSprites;
begin
Piezas.Free;
Temporal.Free;
Fondo.Free;
end;

Otro procedimiento que vamos a crear es el encargado de controlar los eventos en el juego. Como por ahora no vamos a hacer nada, lo dejamos vacío.

procedure ControlarEventos;
begin
end;

Y por último implementamos el procedimiento DibujarSprites que lo único que va a hacer por ahora es dibujar el fondo de pantalla:

procedure DibujarSprites;
begin
Temporal.Dibujar;
end;

Con esto ya podemos ejecutar el juego:


COMO CREAR EL MAPA CON LAS PIEZAS

Como dije al principio, el mapa va a constar de 16 piezas horizontales y 12 piezas verticales. Para almacenar la piezas vamos a crear en la unidad UPlataformas.pas un array bidimensional para almacenar donde va cada pieza:

var
Fondo, Temporal, Piezas: TSprite;
Mapa: array[1..16,1..12] of char;

La pantalla del juego la vamos a almacenar en un archivo de texto llamado pantalla1.txt y va a tener los siguientes datos:

1111111111111111
1000000000000001
1000400000000001
1222222222322221
1000000000300001
1000000000300041
1232222222222221
1030000000000001
1030004000000001
1222222222232221
1000000000030041
1111111111111111

El número 1 representa a los bloques de piedra, el número 2 el suelo de madera, el número 3 las escaleras y el número 4 son las joyas que tenemos que recoger.

El procedimiento CargarPantalla va a recoger los datos del archivo de texto y los va a meter en nuestro array bidimensional:

procedure CargarPantalla( sPantalla: String );
var
S: TStringList;
i, j: Integer;
begin
S := TStringList.Create;
S.LoadFromFile( ExtractFilePath( ParamStr( 0 ) ) + sPantalla );

for j := 1 to 12 do
for i := 1 to 16 do
Mapa[i,j] := S[j-1][i];

S.Free;
end;

Hasta ahora hemos utilizado el método Dibujar de la clase TSprite para dibujar los sprites en pantalla. Necesitamos a hora un método similar que dibuje en la superficie que yo le diga. Para ello vamos a ampliar la clase TSprite que está en la unidad UJuego.pas con el siguiente método:

procedure TSprite.DibujarEn( SuperficieDestino: PSDL_Surface );
var
Origen, Destino: TSDL_Rect;
begin
// ¿Está visible el sprite?
if bVisible then
// ¿Tiene subsprites?
if bSubSprites then
begin
Origen.x := iSubX * iAnchoSub;
Origen.y := iSubY * iAltoSub;
Origen.w := iAnchoSub;
Origen.h := iAltoSub;
Destino.x := x;
Destino.y := y;
Destino.w := iAnchoSub;
Destino.h := iAltoSub;

// Dibujamos el subsprite seleccionado
if SDL_BlitSurface( Superficie, @Origen, SuperficieDestino, @Destino ) < 0 then
begin
ShowMessage( 'Error al dibujar el sprite "' + sNombre + '" en pantalla.' );
Exit;
end;
end
else
begin
Origen.x := 0;
Origen.y := 0;
Origen.w := iAncho;
Origen.h := iAlto;
Destino.x := x;
Destino.y := y;
Destino.w := iAncho;
Destino.h := iAlto;

// Lo dibujamos entero
if SDL_BlitSurface( Superficie, @Origen, SuperficieDestino, @Destino ) < 0 then
begin
ShowMessage( 'Error al dibujar el sprite "' + sNombre + '" en pantalla.' );
Exit;
end;
end;
end;

Este método es similar al anterior pero permite dibujar el sprite en la superficie que le pasemos como parámetro. Esto lo necesitamos para dibujar las piezas del mapa encima de la pantalla temporal, antes de que todo el fondo se dibuje en pantalla. Esto lo vamos a hacer con el procedimiento DibujarPiezasEnTemporal que vamos a definir en la unidad UPlataformas.pas:

procedure DibujarPiezasEnTemporal;
var
i, j: Integer;
begin
for j := 1 to 12 do
for i := 1 to 16 do
begin
Piezas.x := ( i - 1 ) * 40;
Piezas.y := ( j - 1 ) * 40;
Piezas.iSubY := StrToInt( Mapa[i,j] );
Piezas.DibujarEn( Temporal.Superficie );
end;
end;

Después ampliamos el procedimiento CargarSprites para que cargue la pantalla del archivo y la dibuje en la pantalla temporal:

procedure CargarSprites;
begin
Fondo := TSprite.Create;
Fondo.CargarSuperficie( ExtractFilePath( ParamStr( 0 ) ) + 'fondo.jpg' );

Temporal := TSprite.Create;
Temporal.CargarSuperficie( ExtractFilePath( ParamStr( 0 ) ) + 'fondo.jpg' );

Piezas := TSprite.Create;
Piezas.CargarSuperficie( ExtractFilePath( ParamStr( 0 ) ) + 'piezas.png' );
Piezas.bSubsprites := True;
Piezas.iAnchoSub := 40;
Piezas.iAltoSub := 40;

CargarPantalla( 'pantalla1.txt' );
DibujarPiezasEnTemporal;
end;


Y este es el resultado al ejecutar el juego:


Aquí tenéis todo el proyecto (con el ejecutable incluido ) comprimido con zip en tres servidores distintos:

https://mega.nz/file/tRhS1CpQ#3KlM5s6Rda7S2ld765UqgyTSAGw7JoBKzX9SPAjszlI

En el próximo artículo veremos como mover el héroe por pantalla para que recoja las joyas incluyendo rutinas de salto teniendo en cuenta la gravedad.

Pruebas realizadas en Delphi 7.

Publicidad