26 octubre 2007

Moviendo sprites con el teclado y el ratón

Basándome en el ejemplo del artículo anterior que mostraba como realizar el movimiento de sprites con fondo vamos a ver como el usuario puede mover los sprites usando el teclado y el ratón.

CAPTURANDO LOS EVENTOS DEL TECLADO

La clase TForm dispone de dos eventos para controlar las pulsaciones de teclado: OnKeyDown y OnKeyUp. Necesitamos ambos eventos porque no sólo me interesa saber cuando un usuario ha pulsado una tecla sino también cuando la ha soltado (para controlar las diagonales).

Para hacer esto voy a crear cuatro variables booleanas en la sección private el formulario que me van a informar de cuando están pulsadas las teclas del cursor:

type
private
{ Private declarations }
Sprite: TSprite;
Buffer, Fondo: TImage;
bDerecha, bIzquierda, bArriba, bAbajo: Boolean;

Estas variables las voy a actualizar en el evento OnKeyDown:

procedure TFormulario.FormKeyDown( Sender: TObject; var Key: Word; Shift: TShiftState );
begin
case key of
VK_LEFT: bIzquierda := True;
VK_DOWN: bAbajo := True;
VK_UP: bArriba := True;
VK_RIGHT: bDerecha := True;
end;
end;

y en el evento OnKeyUp:

procedure TFormulario.FormKeyUp( Sender: TObject; var Key: Word; Shift: TShiftState );
begin
case key of
VK_LEFT: bIzquierda := False;
VK_DOWN: bAbajo := False;
VK_UP: bArriba := False;
VK_RIGHT: bDerecha := False;
end;
end;

Al igual que hice con el ejemplo anterior voy a utilizar un temporizador (TTimer) llamado TmpTeclado con un intervalo que va a ser también de 10 milisegundos y cuyo evento OnTimer va a encargarse de dibujar el sprite en pantalla:

procedure TFormulario.TmpTecladoTimer( Sender: TObject );
begin
// ¿Ha pulsado la tecla izquierda?
if bIzquierda then
if Sprite.x > 0 then
Dec( Sprite.x );

// ¿Ha pulsado la tecla arriba?
if bArriba then
if Sprite.y > 0 then
Dec( Sprite.y );

// ¿Ha pulsado la tecla derecha?
if bDerecha then
if Sprite.x + Sprite.Imagen.Width < ClientWidth then
Inc( Sprite.x );

// ¿Ha pulsado la tecla abajo?
if bAbajo then
if Sprite.y + Sprite.Imagen.Height < ClientHeight then
Inc( Sprite.y );

DibujarSprite;
end;

Este evento comprueba la pulsación de todas las teclas controlando que el sprite no se salga del formulario. El procedimiento de DibujarSprite sería el siguiente:

procedure TFormulario.DibujarSprite;
var
Origen, Destino: TRect;
begin
// Copiamos el fondo de pantalla al buffer
Origen.Left := Sprite.x;
Origen.Top := Sprite.y;
Origen.Right := Sprite.x + Sprite.Imagen.Width;
Origen.Bottom := Sprite.y + Sprite.Imagen.Height;
Destino.Left := 0;
Destino.Top := 0;
Destino.Right := Sprite.Imagen.Width;
Destino.Bottom := Sprite.Imagen.Height;
Buffer.Canvas.CopyMode := cmSrcCopy;
Buffer.Canvas.CopyRect( Destino, Fondo.Canvas, Origen );

// Dibujamos el sprite en el buffer encima del fondo copiado
Sprite.Dibujar( 0, 0, Buffer.Canvas );

// Dibujamos el contenido del buffer a la pantalla
Canvas.Draw( Sprite.x, Sprite.y, Buffer.Picture.Graphic );
end;

Prácticamente es el mismo visto en el artículo anterior. Ya sólo hace falta poner el marcha el mecanismo que bien podría ser en el evento OnCreate del formulario:

begin
TmpTeclado.Enabled := True;
Sprite.x := 250;
Sprite.y := 150;
end;

CAPTURANDO LOS EVENTOS DEL RATON

Para capturar las coordenadas del ratón vamos a utilizar el evento OnMouseMove del formulario:

procedure TFormulario.FormMouseMove( Sender: TObject; Shift: TShiftState; X, Y: Integer );
begin
Sprite.x := X;
Sprite.y := Y;
end;

Para controlar los eventos del ratón voy a utilizar un temporizador distinto al del teclado llamado TmpRaton con un intervalo de 10 milisegundos. Su evento OnTimer sería sencillo:

procedure TFormulario.TmpRatonTimer( Sender: TObject );
begin
DibujarSprite;
end;

Aquí nos surge un problema importante: como los movimientos del ratón son más bruscos que los del teclado volvemos a tener el problema de que el sprite nos va dejando manchas en pantalla. Para solucionar el problema tenemos que restaurar el fondo de la posición anterior del sprite antes de dibujarlo en la nueva posición..

Para ello voy a guardar en la clase TSprite las coordenadas anteriores:

type
TSprite = class
public
x, y, xAnterior, yAnterior: Integer;
ColorTransparente: TColor;
Imagen, Mascara: TImage;
constructor Create;
destructor Destroy; override;
procedure Cargar( sImagen: string );
procedure Dibujar( x, y: Integer; Canvas: TCanvas );
end;

Al procedimiento DibujarSprite le vamos a añadir que restaure el fondo del sprite de la posición anterior:

procedure TFormulario.DibujarSprite;
var
Origen, Destino: TRect;
begin
// Restauramos el fondo de la posición anterior del sprite
if ( Sprite.xAnterior <> Sprite.x ) or ( Sprite.yAnterior <> Sprite.y ) then
begin
Origen.Left := Sprite.xAnterior;
Origen.Top := Sprite.yAnterior;
Origen.Right := Sprite.xAnterior + Sprite.Imagen.Width;
Origen.Bottom := Sprite.yAnterior + Sprite.Imagen.Height;
Destino := Origen;
Canvas.CopyMode := cmSrcCopy;
Canvas.CopyRect( Destino, Fondo.Canvas, Origen );
end;

// Copiamos el fondo de pantalla al buffer
Origen.Left := Sprite.x;
Origen.Top := Sprite.y;
Origen.Right := Sprite.x + Sprite.Imagen.Width;
Origen.Bottom := Sprite.y + Sprite.Imagen.Height;
Destino.Left := 0;
Destino.Top := 0;
Destino.Right := Sprite.Imagen.Width;
Destino.Bottom := Sprite.Imagen.Height;
Buffer.Canvas.CopyMode := cmSrcCopy;
Buffer.Canvas.CopyRect( Destino, Fondo.Canvas, Origen );

// Dibujamos el sprite en el buffer encima del fondo copiado
Sprite.Dibujar( 0, 0, Buffer.Canvas );

// Dibujamos el contenido del buffer a la pantalla
Canvas.Draw( Sprite.x, Sprite.y, Buffer.Picture.Graphic );

Sprite.xAnterior := Sprite.x;
Sprite.yAnterior := Sprite.y;
end;

Y finalmente activamos el temporizador que controla el ratón y ocultamos el cursor del ratón para que no se superponga encima de nuestro sprite:

begin
TmpRaton.Enabled := True;
Sprite.x := 250;
Sprite.y := 150;
ShowCursor( False );
end;

Al ejecutar el programa podeis ver como se mueve el sprite como si fuera el cursor del ratón.

Aunque se pueden hacer cosas bonitas utilizando el Canvas no os hagais muchas ilusiones ya que si por algo destaca la librería GDI de Windows (el Canvas) es por su lentitud y por la diferencia de velocidad entre ordenadores.

Para hacer cosas serías habría que irse a la librerías SDL (mi favorita), OpenGL o DirectX ( aunque hay decenas de motores gráficos 2D y 3D para Delphi en Internet que simplifican el trabajo).

Pruebas realizadas en Delphi 7.

2 comentarios:

Anónimo dijo...

Hola, la verdad es que me gusta el delphi y estoy intentando usar las librerias sdl, he usado algun ejemplo, pero nose que pasa con la libreria sdl_ttf que no me muestra nada en la pantalla. Alguna idea de que hago mal? o algun ejemplo que tengas.

un saludo y gracias

Delphi al Límite dijo...

Dentro de poco publicaré una serie de artículos dedicados a la programación de videojuegos con Delphi y la librería SDL.

Recibe un cordial saludo.

Publicidad