sábado 17 de septiembre de 2011

SDL Lección 05 - Colisiones

Bienvenido a este quinto tutorial sobre SDL, en este breve post tratare sobre las colisiones entre personajes y algunos objetos del videojuego, para ello re-tomare el ejemplo anterior a la que agregue una función que se encargara de las colisiones, pero esta vez solo trabaje con la clase personaje y parte del main. Este código lo he compilado en Linux y Windows y funciona perfectamente. La única diferencia en Windows seria cambiar las cabeceras "SDL/SDL.h" por "SDL.h" etc. (Esta es la primera revisión, asi que esta sujeta a correcciones de código, pero sobre todo gramática :p). Abajo dejo un enlace para descarar el ejemplo completo.


Irónicamente el fantasma deberia atravesar paredes, pero como tengo flojera de dibuja otra cosa simplemente lo utilizare para mostrar como se lleva a cabo una colisión. 

Comenzare por definir el archivo de cabecera para la función colisión.

//Archivo de Cabecera colision.h
#ifndef _COLISION_H_
#define _COLISION_H_
#include "SDL/SDL.h"


bool colision(SDL_Rect, SDL_Rect);


#endif


Como ven la función bool colision(SDL_Rect, SDL_Rect); simplemente se encarga de recibir dos parámetros, para verificar colisones entre dos "cajas", han de recordar que SDL_Rect es una estructura que maneja puede manejar cuatro miembros: posición en x, posición en y, ancho y alto.

Ahora pasemos al método de la función colisión.


//Archivo colision.cpp
#include "colision.h"

bool colision(SDL_Rect a, SDL_Rect b){

   //Rectangulo a
   int izq_a = a.x;
   int der_a = a.x + a.w;
   int arr_a = a.y;
   int aba_a = a.y + a.h;

   //Rectangulo b
   int izq_b = b.x;
   int der_b = b.x + b.w;
   int arr_b = b.y;
   int aba_b = b.y + b.h;

   if(aba_a <= arr_b || arr_a >= aba_b || der_a <= izq_b || izq_a >= der_b){
      return false;
   }

   else{
      return true;
   }

}

Como pueden ver declaramos cuatro variables para cada una de los parámetros pasados. Por ejemplo: el lado izquierdo de la primera caja es igual a su misma posición en x, entonces para obtener el ancho de la caja hay que tomar su posición en x y simplemente sumarle el ancho que tiene.

Después de haberle asignado lo valores hay que comprobar si cada lado de las dos cajas no chocan, por ejemplo si el lado derecho de la caja "a" es menor al lado izquierdo de "b" significa que no existe colisión, entones devuelve un falso, de los contrario devolverá un valor verdadero.


Ahora es necesario definir las cajas del personaje y de los obstáculos utilizando los archivos que ya tengo de los ejemplos anteriores. También he agregado mas teclas de movimiento para que el personaje se pueda desplazar hacia arriba y hacia abajo, no lo explico pero no tiene mayor complicación que moverse de izquierda a derecha como mostre anteriormente. Ademas de también re-utilize los mismo clips de izquierda y derecha para no tener que dibujar los clip de van hacia arriba y hacia abajo.

Dentro del la definición personaje simplemente he hecho tres cosas: agregar una estructura SDL_Rect pers_caja, que sera la encargada de contener la caja, una función SDL_Rect caja() se encarga de devolver el valor SDL_Rect y por ultimo simplemente moví a la clase publica Uint8 *tecla, que estaba ubicado en la función eventos();

//personaje.h
.
.
.
class Personaje{
   private:
     SDL_Surface *personaje;
      SDL_Rect persIzqClip[4];
      SDL_Rect persDerClip[4];
      SDL_Rect pers_caja;
      int frame;
      bool animar;
   public:
      Personaje();
      int x_persPos, y_persPos;
      int direccion;
      Uint8 *tecla;
      SDL_Rect caja();
      void clips();
      void eventos();
.
.
.

A continuación muestro la nueva funcion caja(), como pueden ver para ubicar la caja de colisión es necesario toma el valor de la posicion de personaje "x_persPos" y se la asignarla a la caja pers_caja.x y haci continuamos con los cuatro parámetros.  Si quieres crear una caja mas pequeña o ubicarla en alguna parte distinta bastaría con solo sumar o restar con respecto a su posición o anchura del personaje.

//personaje.cpp 
.
.
.
SDL_Rect Personaje::caja(){
pers_caja.x = x_persPos;
pers_caja.y = y_persPos;
pers_caja.w = PERS_ANCHO;
pers_caja.h = PERS_ALTO;
return pers_caja;
}
.
.
.

Para las colisiones que definí es necesario tener al menos tener dos cajas para compáralas, una tiene que ser del personaje y otra puede ser un enemigo, en este caso pondre dos obstáculos con los cuales chocara. Los definiré dentro del archivo leccion_05.cpp para evitar que tener hacer otro archivo, pero tal vez lo mas conveniente sea separarlo. 

Agrego el nuevo archivo de cabecera para las colisione y mas abajo declaro dos variables del tipo SDL_Rect los cuales serán los obstáculos y de acuerdo a su ubicación en el dibujo que tengo hecho y les asigno los valores para sus dimensiones. La función choques() contendra el llamado a las cajas a comparar para la colisiones.

//leccion_05.cpp archivo del main
.
.
.
#include "tiempo.h"
#include "colision.h"
#include "personaje.h"
.
.
.
SDL_Rect caja1;
SDL_Rect caja2;

void choques(){

   caja1.x = 240;
   caja1.y = 90;
   caja1.w = 30;
   caja1.h = 30;

   caja2.x = 90;
   caja2.y = 180;
   caja2.w = 60;
   caja2.h = 60;

   if(colision(Personaje.caja(), caja1)){

      if(Personaje.direccion == PERS_DER){
         Personaje.x_persPos -= PERS_AVANZA;
         Personaje.tecla[SDLK_RIGHT] = 0;
}

     if(Personaje.direccion == PERS_IZQ){
         Personaje.x_persPos += PERS_AVANZA;
         Personaje.tecla[SDLK_LEFT] = 0;
     }
.
.
.

En el código de arriba pueden observar que llamo a la nueva función colisión() y llamo a una de las dos cajas y que quiero que compare, en este caso Personaje.caja() y caja1. (Recordar que Personaje.caja() retorna un valor de tipo SDL_Rect). Después de ello hay que indicarle que es lo que queremos que haga cuando ocurra la colisión, en este caso contrarresta el avanze, desincrementado el valor o incrementado el valor de la posición según sea el caso, ya por ultimo hago que el valor de presionar la tecla sea falso, todo esto depende de lo que queramos hacer dentro de nuestra condicion de colisión.

Por ultimo agrego la función choques() dentro del bucle del juego.
.
.
.
   Personaje.eventos();
   //Llamamos a las colisiones dentro del bucle
   choques();
   SDL_BlitSurface(imagen, NULL, pantalla, NULL);
   Personaje.dibuja();
.
.
.
Básicamente esta es una manera de como se pueden llevar a cabo las colisiones entre un personaje y algún objeto como una pared o enemigo etc.


Anexo:
En linux si compilas usando el compilador gcc es necesario indicarle todos los archivos y librerías a  utilizar, creando un makefile simplificaría las cosas pero por el momento simplemente lo compilare desde consola.

Primero creare los modulos con:

g++ -c leccion_05.cpp personaje.cpp tiempo.cpp colision.cpp
Y luego enlazare los modulos con:
g++ -o leccion_05 -lSDL -lSDL_image leccion_05.o personaje.o tiempo.o colision.o
Y para ejecutarlo solo basta darle doble click al ejecutable o escribir en consola:
./leccion_05
Descargar el codigo fuente: Leccion_05

0 comentarios:

Publicar un comentario en la entrada