Produciendo y detectando los goles

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


¡Gol!

Si nuestro propósito es crear un marcador que contabilice el número de goles anotado por cada jugador, lo primero que necesitamos es ser capaces de detectar los goles.


Para detectar los goles vamos a utilizar Eventos.


¿Qué es un evento?

En Programación Orientada a Objetos, un evento es un suceso que ocurre en un determinado momento y que podemos detectar.


Simple, ¿no? Se produce un gol => Lo detecto => Lo reflejo en el marcador.


Definición de GolEvent

Lo primero que debemos hacer es definir el Evento. El Evento, como cualquier otro concepto u objeto en POO, es una clase. Pero, para especificar que es un evento, ésta debe heredar de la clase de ActionScript Event.


Para mantener el pulcro orden que hemos intentado llevar hasta ahora, vamos a crear una nueva subcarpeta en pong llamada evento, y en ella vamos a crear la clase GolEvent. Pero antes, un pequeño concepto para entender el código de GolEvent:


¿Qué es un atributo estático (static) o de clase?

Es un atributo global compartido por todas las instancias (objetos) de una clase. Su definición es la siguiente:


visibilidad static atributo_de_clase:Tipo = valor;

Es decir, con static especificamos que se trata de un atributo de clase. ¿Y para qué sirve esto? Pongamos un ejemplo.


public class MiClase
{
public static var numero:Number = 0;
}

Ahora, sólo con importar la clase MiClase tendríamos acceso a "MiClase.numero" sin necesidad de crear ningún objeto de esta clase. Por ejemplo, el código:


trace(MiClase.numero);

tendría como salida:


0

Hay algunos detalles más detrás de los miembros static (cómo que las funciones también pueden serlo), pero de momento esto nos sirve para continuar.


Ahora sí, la definición de GolEvent:


package mi.pong.evento
{
import flash.events.Event;

/**
* ...
* @author ASL
*/
public class GolEvent extends Event
{
public static const GOL_EVENT:String = "GolEvent";
public static const IZQUIERDA:uint = 0;
public static const DERECHA:uint = 1;

public var lado:uint;

public function GolEvent(lado_gol:uint)
{
super(GOL_EVENT);
lado = lado_gol;
}

}

}

- GOL_EVENT:String: Este es el "nombre del evento". La clase Event "nos obliga" a definir un string identificativo para los eventos. Este string se envía al constructor padre a través de super(GOL_EVENT); Posteriormente, este string nos servirá para detectar el evento. El modificador const (en contraste con var) especifica que el valor de este atributo no puede ser cambiado.


- IZQUIERDA, DERECHA: Estas dos constantes nos servirán para determinar en cuál de los dos lados se produjo el gol, para poder sumarlo al marcador correspondiente.


- lado: Esta atributo contendrá el lado en el que se produjo el gol (IZQUIERDA o DERECHA).


- En el constructor, lo primero que hacemos es llamar al constructor padre (el de la clase Event) con en el nombre del evento. Esto debemos hacerlo obligatoriamente siempre. Lo hacemos con la palabra especial super, que explicaré a continuación. Después asignamos al atributo lado el lado desde el que se produjo el gol, que viene dado en forma de parámetro en el constructor.


- Notad que, por convenio, las constantes van escritas con mayúsculas.


¿Para qué sirve la palabra clave "super"?

La palabra super nos da acceso desde una clase hija a los miembros de su clase padre. Es decir, si yo tengo una clase "Transporte" con una función "viajar" y quiero acceder a ella desde su clase hija "Avión" puedo hacerlo con super.viajar().


En verdad, super no se utiliza con este fin, puesto que se puede acceder a viajar (y a cualquier otro miembro de Transporte) sin necesidad de especificarlo.


El único caso de uso real es en el del constructor. Aunque no se especifique textualmente, internamente, todos los constructores de clases heredades definen como primera instrucción super(), que llama al constructor de la clase padre. Cuándo lo definimos explícitamente, como en el caso de GolEvent es porque necesitamos especificar parámetros para el constructor de la clase padre.


¿Quién produce el GolEvent?

El siguiente paso es producir el evento para posteriormente detectarlo.


Aunque se podría hacer de diferentes maneras, de todos los elementos que de momento tenemos en nuestro juego, el que más sentido tiene que produzca los eventos de Gol es la Bola. Al fin y al cabo, es la que mejor sabe cuándo ha llegado a los confines del escenario.


Añadimos el código necesario en la función loop de Bola, en dónde ya detectamos cuándo llega a los límites del campo:


  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();
dispatchEvent(new GolEvent(GolEvent.DERECHA));
vx = 5;
vy = 0;
}
else if (x > stageRef.stageWidth)
{
iniciaPosicion();
dispatchEvent(new GolEvent(GolEvent.IZQUIERDA));
vx = -5;
vy = 0;
}
}

- dispatchEvent(new GolEvent(GolEvent.DERECHA)): esta función (heredada de MovieClip) permite a la bola lanzar un evento que alguien puede escuchar. Básicamente esto se traduce en: "Oye, mira, soy la bola. Que ha habido gol por la derecha." Por la izquierda es análogo.


La clave ahora está en detectar ese evento y procesarlo.


¿Quién detecta el GolEvent?

Cómo la idea era y es que el Escenario se encargue de la mayor lógica del juego posible, será él quién detecte el evento. La manera de detectar los eventos es algo que ya hemos hecho en varias ocasiones, con la función addEventListener(...). Vamos a realizar una modificación en la función creaBolas del Escenario:


  public function creaBolas(num_bolas:uint)
{
for (var i:uint = 0; i < num_bolas; i++)
{
var bola:Bola = new Bola(stageRef);
bola.addEventListener(GolEvent.GOL_EVENT, golAnotado, false, 0, true);
stageRef.addChild(bola);
colisionadores.push(bola);
}
}

- bola.addEventListener(GolEvent.GOL_EVENT, golAnotado, ...): Añadimos un eventListener ("oyente de eventos") a la bola, que queda a la expectativa de que ésta haga un dispatchEvent(...) que contenga un GolEvent para ejecutar la función golAnotado.


Cobra aquí importancia el atributo estático GOL_EVENT, y cómo lo especificamos en la clase padre Event (a través de super(GOL_EVENT)), convirtiéndolo así en el "nombre del evento". Ahora en addEventListener indicamos que el evento que nos interesa detectar es el que lleva ese nombre.


Ahora sólo nos queda definir la función golAnotado:


  public function golAnotado(e:GolEvent)
{
trace("¡Gol!");
}

- golAnotado(e:GolEvent): Al utilizarla en addEvenlistener, la función golAnotado recibe como parámetro un evento (en este caso, de tipo GolEvent).


De momento, esto nos sirve para comprobar si todo el flujo de eventos funciona. En el siguiente capítulo crearemos los marcadores.








Anterior Índice Siguiente