3.III. Detección de colisiones

Esta entrada pertenece a ActionScript 3 - Guía para Principiantes.

Resultado al final del capítulo (Click sobre Flash para jugar; Controles: w, s, flecha arriba, flecha abajo):




La clase Bola


Algo fundamental para que nuestro juego tenga algo de sentido es una bola que rebote de pong en pong. Breve recordatorio (y último detallado) de los pasos a seguir a la hora de crear una nueva clase que tenga una representación gráfica:


1) Creación de la clase ActionScript: Con nuestro proyecto Pong abierto en FlashDevelop, click derecho en la carpeta pong -> Add... -> New Class... -> Bola.as. Y para empezar, escribimos el siguiente código en el archivo:


package mi.pong
{
import flash.display.MovieClip;
import flash.display.Stage;
import flash.events.Event;

/**
* ...
* @author ASL
*/
public class Bola extends MovieClip
{
private var vx:Number = 5;
private var vy:Number = 0;
private var max_velocidad:Number = 2;

private var stageRef:Stage;

public function Bola(stage:Stage)
{
stageRef = stage;

addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
iniciaPosicion();
}

public function iniciaPosicion()
{
x = stageRef.stageWidth / 2;
y = stageRef.stageHeight / 2;
}

public function loop(e:Event)
{
x += vx;
y += vy;

if (y < 0)
{
y = 0;
vy = -vy;
}
else if (y > stageRef.stageHeight)
{
y = stageRef.stageHeight;
vy = -vy;
}

if (x < 0)
{
iniciaPosicion();
vx = 5;
vy = 0;
}
else if (x > stageRef.stageWidth)
{
iniciaPosicion();
vx = -5;
vy = 0;
}
}

}

}


Espero que no resulte demasiado extraño al lector el código escrito para la clase Bola. Tiene bastantes puntos comunes con el código de la clase Pong. Al igual que los pongs, la bola también tiene una velocidad, aunque esta vez formada por dos componentes (ya que puede moverse hacia todas partes), la del eje x "vx" y la del eje y "vy". También una velocidad máxima "max_velocidad", que utilizaremos en el futuro para "calibrar" su velocidad.


El último bloque de la función loop (que es la función que se ejecuta cada vez que comienza un nuevo frame) controla que la bola no salga de los límites del eje x de la película, devolviendo a ésta al centro del campo.


2) Creación del símbolo Flash: En la ventana Flash -> Insertar -> Nuevo símbolo... y utilizamos las siguientes opciones:



y para el símbolo dibujamos un simple círculo blanco (ya habrá tiempo para mejorar la apariencia de nuestro juego) con las propiedades de la imagen:


Para añadir nuestra bola al juego añadimos lo siguiente en la función Main:

...
var bola:Bola = new Bola(stage);
addChild(bola);
...


Empiezo a ir un poco rápido porque entiendo que después de todo el rollo soltado en capítulos anteriores, el lector entiende más o menos todo lo que estamos haciendo. Si queda cualquier duda, siempre puedes preguntarla en los comentarios.


Bien, si ejecutamos nuestro Flash veremos nuestra bola moviéndose de un lado otro sin mucho sentido. Ahora, lo que necesitamos, es que colisione con los pongs.


Detección de colisiones


En el problema de las colisiones entre objetos es uno de los problemas tradicionales en la programación de cualquier tipo de videojuego. Son muchas las maneras en las que puede ser abordado y resuelto, y en esta guía he optado por una en particular, que para este caso y desde mi punto de vista, es la más sencillo y adecuado.


La técnica consiste en definir un área, dentro del objeto, que sea "capaz de colisionar". Es decir, que aunque gráficamente dos objetos estén superpuestos, sólo se produzca colisión si sus áreas de colisión están superpuestas.


Las colisiones, de manera gráfica (la zona más oscurá representa la zona de colisión de cada objeto):



En la primera, obviamente, no hay colisión. En la segunda, la bola está sobre el gráfico que representa el pong, pero no sobre su zona de colisión. En la última, sendas zonas de colisiones se superponen, produciéndose el choque.


¿Y cómo vamos a representar esta zona de colisiones? Pues, saliéndonos un poco (muy poco, ¿eh?) de aquello que dijimos de que "todo aquello que pueda hacerse desde ActionScript será hecho desde ActionScript".


Nuestra zona de colisiones va a ser un nuevo símbolo al que vamos a llamar "HitBox" cuyo único contenido va a ser un cuadrado sin borde y de un color semitransparente. Este símbolo no va a estar asociado a ninguna clase ActionScript.



Ahora necesitamos definar la zona de colisión en los símbolos para la bola y el pong.


1) En la ventana de Biblioteca (Ctrl+L), click derecho en el símbolo Pong -> Edición... Crea una nueva capa llamada hitBox y selecciónala. Ahora arrastra desde la biblioteca un símbolo "HitBox" hasta el centro del Pong.


2) Con la herramienta de transformación libre (Q) ajustamos el cuadrado a la zona de colisión buscada.


3) Nombramos a la instancia del símbolo como "hitBox". Este paso es muy importante, porque será el que nos dé acceso a la zona de colisión desde el código ActionScript.


Seguimos el mismo proceso para la bola.


¿Cómo detecto colisiones en ActionScript?
object1.hitTestObject(object2)


Que devuelve true si los objetos object1 y object2 han colisionado.


Vamos a pensar un poco la lógica que queremos detras de nuestras colisiones: De momento, sólo tenemos un objeto que puede colisionar: la bola, y dos objetos sobre los que colisionar, los pongs. Entonces, sólo necesitamos que la bola sepa sobre que objetos puede colisionar. Esto lo vamos a conseguir pasándole un Array (una "lista") con ambos pongs, en la función Main, por supuesto. Nuestra función tendrá este aspecto:


  public function Main()
{
key = new KeyObject(stage);
var colisionadores:Array = new Array();

var miPong1:Pong = new Pong(stage, 0, key);
addChild(miPong1);
colisionadores.push(miPong1);

var miPong2:Pong = new Pong(stage, 1, key);
addChild(miPong2);
colisionadores.push(miPong2);

var bola:Bola = new Bola(stage, colisionadores);
addChild(bola);
}


- colisionadores.push(miPong1); Con la función push añadimos un elemento al Array.


En verdad, estamos repitiendo código, y podemos agrupar la creación de pongs en un bucle:


  public function Main()
{
key = new KeyObject(stage);
var colisionadores:Array = new Array();

for (var i:uint; i < 2; i++)
{
var miPong:Pong = new Pong(stage, i, key);
addChild(miPong);
colisionadores.push(miPong);
}

var bola:Bola = new Bola(stage, colisionadores);
addChild(bola);
}


Ahora debemos modificar la clase Bola para que procesa las colisiones. Añadimos un atributo para guardar la lista de colisionadores:


private var colisionadores:Array;
...
public function Bola(stage:Stage, lista_colisiones:Array)
{
stageRef = stage;
colisionadores = lista_colisiones;
addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
iniciaPosicion();
}
...


Y el procesamiento de colisiones en la función loop:


public function loop(e:Event)
{
for (var i:uint = 0; i < colisionadores.length; i++)
{
if (hitBox.hitTestObject(colisionadores[i].hitBox))
{
vx = -vx;
vy = ((y - colisionadores[i].y) / colisionadores[i].width*2) * max_velocidad;
}
}

x += vx;
y += vy;
...

Y aquí entra en juego el nombre de instancia "hitBox", que fue cómo nombramos a nuestro cuadradito semitransparente. Nombrando la instancia como hitBox conseguimos tener un nuevo atributo en la clase correspondiente que representa nuestra zona de colisión. Por eso realizamos el test sobre ellas.


En hitBox.hitTestObject(colisionadores[i].hitBox) ,el primer "hitBox" está haciendo referencia a la zona de colisión de la bola, y el segundo, al del colisionador, en este caso, cualquiera de los pongs.


Si se produce una colisión, cambiamos las componentes de velocidad de la bola.


Toques finales

Ya podríamos ejecutar nuestro juego pong y sería completamente funcional. Pero vamos a añadir un par de detalles para darle algo de vistosidad:


1) Seleccionar un color totalmente transparante (con Alfa a 0%) para el símbolo HitBox y evitar que aparezca en pantalla.


2) Elegir un color verde para el fondo de la película (puedes hacerlo desde Propiedades del Documento).


3) Ajustar la velocidad de la película a 30 FPS (con el documento seleccionado, en la ventana de propiedades, o en la línea de tiempo). Esta velocidad dará una mejor sensación de fluidez.


4) Y cómo extra, he añadido en una nueva capa una sombra para el Pong, copiando la forma del pong y desplazándola ligeramente, borrando el contorno y dándole un color grisáceo semitransparente.

Hasta aquí el desarrollo de nuestro Pong Básico.


En el siguiente capítulo encontrarás los archivos fuente del proyecto.









Anterior Índice Siguiente