04 julio 2008

Programar videojuegos con la librería SDL (y 11)

Hoy voy a terminar la sección dedicada a la programación con SDL en Delphi hablando de la creación de enemigos en un juego de plataformas.

CREANDO LOS ENEMIGOS

Para crear los sprites enemigos voy a utilizar este gráfico:


Como en el juego voy a insertar 6 enemigos, no es necesario cargar 6 sprites con los mismos gráficos. Al igual que hicimos en el juego de aviones, con un solo sprite enemigo podemos dibujar el resto.

Para ello voy a crear una nueva estructura de datos para guardar la posición de cada enemigo y su estado:

TEnemigo = record
x, y, iSubX, iSubY: Integer;
iPosicion: Integer;
bActivo, bIzquierda: Boolean;
end;

Este registro va a guardar las coordenadas de un enemigo, los subsprites que va a dibujar en ese momento, la posición al andar, si está activo y si anda hacia la izquierda o hacia la derecha.

Al igual que hicimos con los aviones vamos a añadir un array global en la unidad UPlataforma.pas para controlar a varios enemigos:

var
Fondo, Temporal, Piezas, Heroe, Enemigo: TSprite;
Enemigos: array[1..5] of TEnemigo;
...

También vamos a ampliar nuestro procedimiento cargar sprites para cargar los enemigos:

procedure CargarSprites;
begin
...
Enemigo := TSprite.Create;
Enemigo.CargarSuperficie( ExtractFilePath( ParamStr( 0 ) ) + 'enemigo.png' );
Enemigo.bSubsprites := True;
Enemigo.iAnchoSub := 40;
Enemigo.iAltoSub := 40;

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

Y por supuesto no nos olvidamos de liberarlo de memoria:

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

SITUANDO LOS ENEMIGOS EN PANTALLA

Una vez ya tenemos nuestro sprite enemigo creado vamos a crear un procedimiento encargado de colocar los enemigos en pantalla:

procedure SituarEnemigos;
var
i: Integer;
begin
Enemigos[1].x := 40;
Enemigos[1].y := 320;
Enemigos[2].x := 400;
Enemigos[2].y := 320;
Enemigos[3].x := 440;
Enemigos[3].y := 200;
Enemigos[3].bIzquierda := True;
Enemigos[4].x := 160;
Enemigos[4].y := 200;
Enemigos[4].bIzquierda := True;
Enemigos[5].x := 200;
Enemigos[5].y := 80;
Enemigos[6].x := 440;
Enemigos[6].y := 80;

for i := 1 to 6 do
Enemigos[i].bActivo := True;
end;

A este procedimiento lo podemos llamar al final del procedimiento CargarSprites:

procedure CargarSprites;
begin
...
CargarPantalla( 'pantalla1.txt' );
DibujarPiezasEnTemporal;
SituarEnemigos;
end;

Lo más difícil es lo que viene ahora. Tenemos que mover todos los enemigos a la vez:

procedure MoverEnemigos;
var
i, xMapa, yMapa: Integer;
begin
for i := 1 to 6 do
begin
yMapa := ( Enemigos[i].y + 40 ) div 40 + 1;

if Enemigos[i].bActivo then
begin
if Enemigos[i].bIzquierda then
begin
Enemigos[i].iSubY := 1;
Dec( Enemigos[i].x );
xMapa := ( Enemigos[i].x - 1 ) div 40 + 1;
if ( Mapa[xMapa,yMapa] = '0' ) or ( Mapa[xMapa,yMapa] = '1' ) then
Enemigos[i].bIzquierda := False;
end
else
begin
Enemigos[i].iSubY := 0;
Inc( Enemigos[i].x );
xMapa := ( Enemigos[i].x + 40 ) div 40 + 1;
if ( Mapa[xMapa,yMapa] = '0' ) or ( Mapa[xMapa,yMapa] = '1' ) then
Enemigos[i].bIzquierda := True;
end;

// cambiamos de fotograma
Inc( Enemigos[i].iPosicion );

if Enemigos[i].iPosicion > 4 then
begin
if Enemigos[i].iSubX = 0 then
Enemigos[i].iSubX := 1
else
Enemigos[i].iSubX := 0;

Enemigos[i].iPosicion := 0;
end;
end;
end;
end;

Este procedimiento recorre todos los enemigos y los mueve a la derecha o hacia la izquierda hasta que choquen con un obstáculo. Cuando choca cambia de dirección hasta que vuelva a chocar. También nos encargamos de cambiar de fotograma para que se produzca la animación al andar.

Ahora sólo tenemos que modificar el procedimiento DibujarSprites para que dibuje a los enemigos y los mueva:

procedure DibujarSprites;
var
i: Integer;
begin
Temporal.Dibujar;
Heroe.Dibujar;

for i := 1 to 6 do
if Enemigos[i].bActivo then
begin
Enemigo.x := Enemigos[i].x;
Enemigo.y := Enemigos[i].y;
Enemigo.iSubX := Enemigos[i].iSubX;
Enemigo.iSubY := Enemigos[i].iSubY;
Enemigo.Dibujar;
end;

MoverEnemigos;
end;

Este sería el resultado:


COMPROBANDO LAS COLISIONES ENTRE SPRITES

Por último vamos a hacer que si el sprite del héroe choca con algún enemigo tenga que volver a empezar desde el principio (incluyendo volver a recoger las joyas). Eso lo vamos a hacer con el siguiente procedimiento:

procedure ComprobarEnemigos;
var
i: Integer;
begin
for i := 1 to 6 do
begin
if Enemigos[i].bActivo then
if ( Heroe.x + 20 >= Enemigos[i].x ) and ( Heroe.x + 20 <= Enemigos[i].x + 39 ) and
( Heroe.y + 20 >= Enemigos[i].y ) and ( Heroe.y + 20 <= Enemigos[i].y + 39 ) then
begin
// volvemos a colocar el equipo y los enemigos
Heroe.rx := 40;
Heroe.ry := 400;
SituarEnemigos;
CargarPantalla( 'pantalla1.txt' );

// volvemos a redibujar la pantalla de fondo
Fondo.DibujarEn( Temporal.Superficie );
DibujarPiezasEnTemporal;
end;
end;
end;

Para comprobar si el héroe choca con el enemigo lo que hago es comprobar si el centro de las coordenadas del héroe (heroe.x + 20, heroe.y + 20 ) están dentro de la cuadrícula de 40 x 40 que forma parte el enemigo. Cuando chocan entonces vuelvo a colocar al héroe, a los enemigos y las joyas, volviendo a dibujar la pantalla de fondo.

Este procedimiento hay que llamarlo desde DibujarSprites:

procedure DibujarSprites;
begin
...
MoverEnemigos;
ComprobarEnemigos;
end;


Otras cosas que se le podían haber añadido al juego es el control de vidas de modo que cada vez que nos maten decremente una vida. También haría falta meterle sonidos y una música de fondo, pero la falta de tiempo me impide cerrar el círculo.

Aquí os dejo el proyecto en RapidShare:

https://mega.nz/file/5FhU2SyD#ZavwnR9Amwcpub4wGaTI5zLsqEOPmUxuy201rtIUaOA

EL FIN DE UNA SERIE

Con esto doy más o menos por finalizado el curso de introducción a la programación de videojuegos con la librería SDL. Podía haber hecho muchos más ejemplos (juegos de estrategia, juegos de carreras, juegos de inteligencia tipo tetris, etc.), pero la falta de tiempo me impide llevarlo a cabo. Quizás en un futuro si encuentro un buen patrocinador que haga que no vuelva a trabajar podría abarcar todos estos temas (que bueno es soñar, jejeje).

Otro tema que también dejo pendiente para un futuro es la programación de videojuegos 3D con OpenGL, donde la librería SDL también da un buen soporte, pero todavía no lo tengo muy preparado y me gustaría presentarlo bien, ya que es un tema algo complicado pero apasionante.

Aprovechando también la librería OpenGL veremos también como realizar juegos 2D utilizando polígonos, lo que hace que los juegos sean espectaculares con sprites supergigantes y con los efectos que brindan las tarjetas 3D (iluminación, realzado de texturas, rotaciones, etc.).

Cuando tenga una librería bien montada crearé otra serie de artículos dedicados a la OpenGL en Delphi. En el próximo artículo voy a comenzar hablando de las novedades de RAD Studio 2007 respecto a Delphi 7. Ya va siendo hora de guardar nuestro querido Delphi 7 en el cajón y aprovechar las nuevas ventajas del nuevo IDE de CodeGear. Con Embarcadero Technologies comienza una nueva era para Delphi. Espero que sea para mejor.

Pruebas realizadas en Delphi 7.

Publicidad