HTC Sync no sincroniza con HTC con Android

Uno de los problemas comunes que suelen surgir cuándo se instala el HTC Sync desde un dispotivio HTC es que no es posible encontrar el driver necesario para la sincronización.


Este problema deriva de la configuración en español de Windows, ya que el driver es buscado en la carpeta C:\Programfiles\HTC\HTC Driver\Driver Files\XP_x86, cuándo, en verdad y debido al idioma, está en C:\Archivos de programa\HTC\HTC Driver\Driver Files\XP_x86.


La solución es bien sencilla:



  1. Click derecho sobre Mi PC -> Administrar

  2. Pulsamos en Administrador de dispositivos y buscamos ABD, que es el dispositivo que falla.

  3. Click derecho sobre él, e instalar controlador.

  4. Elegís buscar el controlador manualmente, y seleccionáis la carpeta en la que se encuentra: C:\Archivos de programa\HTC\HTC Driver\Driver Files\XP_x86

  5. Pulsáis finalizar, y todo debería funcionar perfectamente

La ciencia en España no necesita tijeras


Me uno, (aquí y en Metafísica de Costumbres) hoy miércoles, 7 de octubre, a la iniciativa de La Aldea Irreductible ante la intención de este Nuestro Gobierno de reducir en un 15% los fondos para investigación.

Es decir, frenar un poquito más el avance en materia científica en este país. No sé cuál es el plan (ni si existe), no ya a corto, sino a largo plazo, para que España salga de este agujero, pero recortar en investigación y desarrollo (en conocimiento, al fin y al cabo) no parece el mejor camino.


Conclusión y archivos fuente Pong 1.1

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


La idea es que esta guía no acabe aquí, pero obligaciones de diferente índole me impiden dedicarle más tiempo, al menos de momento. De todos modos, si has logrado llegar hasta quí paso a paso, habrás aprendido lo suficiente como para seguir explorando Flash, ActionScript 3 y todas sus posibilidades.


Aquí están los archivos fuente del Pong 1.1


Cualquier comentario, error o sugerencia será bien recibido.







Anterior Índice

Un campo de fútbol

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


Para concluir con este capítulo, vamos a crear un escenario algo más elaborado: un campo de fútbol.


La clase CampoFutbol

Dentro de la carpeta escenario vamos a crear una nueva clase que llamaremos CampoFutbol cuyo código será:


package mi.pong.escenario 
{
import com.senocular.utils.KeyObject;
import flash.display.GradientType;
import flash.display.Sprite;
import flash.display.Stage;

/**
* ...
* @author ASL
*/
public class CampoFutbol extends Escenario
{
private var verdeG1_1:uint = 0x01B101;
private var verdeG1_2:uint = 0x02D202;
private var verdeG2_1:uint = 0x25FC25;
private var verdeG2_2:uint = 0x00FB00;

public function CampoFutbol(stage:Stage, keyObject:KeyObject)
{
super(stage, keyObject);
}

public override function creaEscenario()
{
var nZonas:uint = 8;
var anchoZona:Number = stageRef.stageWidth / nZonas;

for (var i:uint = 0; i < nZonas; i++) {
var zona:Sprite = creaZona(i, anchoZona);
zona.y = 0;
zona.x = (stageRef.stageWidth / nZonas) * i;
addChild(zona);
}

dibujaLineas();
}

private function creaZona(i:uint, anchoZona:Number):Sprite
{
var zona:Sprite = new Sprite();
if ( i % 2 != 0)
zona.graphics.beginGradientFill(GradientType.LINEAR, [verdeG1_1, verdeG1_2], [1, 1], [0, 255]);
else
zona.graphics.beginGradientFill(GradientType.LINEAR, [verdeG2_1, verdeG2_2], [1, 1], [0, 255]);

zona.graphics.drawRect(0, 0, stageRef.stageWidth / 8, stageRef.stageHeight);
zona.graphics.endFill();
return zona;
}

private function dibujaLineas():void
{
var margen:Number = 15;
var anchoLinea:Number = 5;
var linea:Sprite = new Sprite();
linea.graphics.beginFill(0xFFFFFF);
// Línea del centro
linea.graphics.drawRect(stageRef.stageWidth / 2 - anchoLinea / 2, margen, anchoLinea, stageRef.stageHeight - 2 * margen);
// Líneas de fondo
// Izquierda
linea.graphics.drawRect(margen, margen, anchoLinea, stageRef.stageHeight - 2 * margen);
// Derecha
linea.graphics.drawRect(stageRef.stageWidth - margen - anchoLinea, margen, anchoLinea, stageRef.stageHeight - 2 * margen);
// Líneas de banda
// Superior
linea.graphics.drawRect(margen, margen - anchoLinea, stageRef.stageWidth - margen * 2, anchoLinea);
// Inferior
linea.graphics.drawRect(margen, stageRef.stageHeight - margen, stageRef.stageWidth - margen * 2, anchoLinea);
linea.graphics.endFill();
addChild(linea);

// Círculo central
var centro:Sprite = new Sprite();
var radioCentro:Number = stageRef.stageHeight / 3;
centro.graphics.beginFill(0xFFFFFF);
centro.graphics.drawEllipse(stageRef.stageWidth / 2 - radioCentro / 2, stageRef.stageHeight / 2 - radioCentro / 2, radioCentro, radioCentro);
centro.graphics.drawEllipse(stageRef.stageWidth / 2 - radioCentro / 2 + anchoLinea, stageRef.stageHeight / 2 - radioCentro / 2 + anchoLinea, radioCentro - anchoLinea*2, radioCentro - anchoLinea*2);
centro.graphics.endFill();
addChild(centro);
}

}

}

Dos cosas importantes en este código:


- La clase CampoFutbol es heredera de la clase Escenario, con lo cual, hereda todas sus funciones. Sin embargo, nosotros queremos redefinir una de las funciones: creaEscenario, porque queremos que la creación de este escenario sea distinta a la del padre. Para realizar este cometido utilizamos la palabra clave override, que "sobreescribe" al método del padre. Es decir, será este método el que se utiliza y no el heredado.


- Todas las funciones utilizadas en el atributo graphics corresponden a métodos de dibujado que proporciona Flash. Podéis encontrar más información aquí.


Ahora, para verlo en acción, ve a la clase Main y cambia la clase al escenario:


var escenario:Escenario = new CampoFutbol(stage, key);

Puedes encontrar los archivos de este proyecto terminado en el siguiente capítulo.








Anterior Índice Siguiente

Añadiendo los Marcadores

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


Nuestro marcador Flash

Para la creación de nuestro marcador, vamos a utilizar únicamente un texto dinámico de Flash, que variará según varíe el número de goles de cada jugador.


Así que, creamos un nuevo símbolo en Flash: menú Insertar -> Nuevo símbolo... De nombre Marcador y de tipo Clip de película. De momento, como aún no la hemos creado, no asociamos el símbolo con ninguna clase ActionScript.


Ahora, escogemos la Herramienta de Texto (T), clickamos en el centro de nuestro símbolo recién creado y escribimos un 0.


En la ventana propiedades, hemos de asegurarnos de que es un Texto dinámico (lo cual nos permitirá variarlo mientras se esté ejecutando Flash. Como nombre de instancia (que nos servirá para acceder al texto desde código ActionScript) escribimos puntuacion_text.


Puedes escoger el formato que quieras para el texto. Yo he elegido como Familia: Tahoma; Estilo: Regular; Tamaño: 30.0 pt y Color: Blanco.


Y ya tenemos nuestro marcador.


Código del marcador

En FlashDevelop, creamos una clase llamada Marcador dentro de la carpeta objeto, pero fuera de colisionable y colsionador. El código ActionScript para Marcador será el siguiente:


package mi.pong.objeto
{
import flash.display.MovieClip;
import flash.display.Stage;

/**
* ...
* @author ASL
*/
public class Marcador extends MovieClip
{
protected var id:uint; // Identificador para el marcador, normalmente el número de jugador
protected var puntuacion:int = 0;

/**
* Constructor del Marcador.
* @param stage Stage de referencia.
* @param id_jugador Jugador al que representa.
*/
public function Marcador(stage:Stage, id_jugador:uint)
{
id = id_jugador;
iniciaPosicion(stage);
}

/**
* Establece una nueva puntuación en el marcador.
* @param nueva_puntuacion Nueva puntuación.
*/
public function setPuntuacion(nueva_puntuacion:int)
{
puntuacion = nueva_puntuacion;
puntuacion_text.text = puntuacion.toString();
}

/**
* Suma la puntuación a la ya acumulada.
* @param acum_puntuacion Puntuación a sumar.
*/
public function sumaPuntuacion(acum_puntuacion:int)
{
puntuacion += acum_puntuacion;
puntuacion_text.text = puntuacion.toString();
}

/**
* Inicia la posición del marcador, en relación a un Stage de
* referencia.
* @param stage Stage de referencia.
*/
public function iniciaPosicion(stage:Stage)
{
puntuacion_text.text = puntuacion.toString();
var separacion:uint = 15;
x = (stage.stageWidth / 2) + ((this.width + separacion) * id) - (this.width + separacion) / 2;
y = width / 2 + separacion;
}

}

}

- Para cambiar el texto del marcador que hemos creado en Flash, utilizamos puntuacion_text.text. Recuerda que puntuacion_text es el nombre que dimos a la instancia de texto dinámico en el marcador.


Añadiendo los marcadores al escenario

Lo primero, en la clase Escenario creamos un Vector que albergue los maracadores:


protected var marcadores:Vector.<Marcador>;

Y modificamos el constructor:


  public function Escenario(stage:Stage, keyObject:KeyObject) 
{
stageRef = stage;
key = keyObject;
colisionables = new Vector.<IColisionable>;
colisionadores = new Vector.<IColisionador>;
marcadores = new Vector.<Marcador>;
creaPongs(2);
creaMarcadores(2);
creaEscenario();
creaBolas(1);
addEventListener(Event.ENTER_FRAME, bucle, false, 0, true);
}

dónde, aparte de crear el Vector de marcadores, además llamamos a la nueva función creaMarcadores cuya definición es la siguiente:


  /**
* Añade un número de marcadores al escenario.
* @param num_marc Número de marcadores.
*/
public function creaMarcadores(num_marc:uint)
{
for (var i:uint = 0; i < num_marc; i++)
{
var marcador:Marcador = new Marcador(stageRef, i);
stageRef.addChild(marcador);
marcadores.push(marcador);
}
}

Y ahora, ¿recuerdas que en GolEvent teníamos un atributo lado que indicaba dónde se produjo el gol? Pues ha llegado el momento de utilizarlo. Podemos borrar el contenido de la función golAnotado en Escenario y cambiarlo por este otro:


  /**
* Tratamiento tras que se produza un evento.
* @param e El evento producido.
*/
public function golAnotado(e:GolEvent)
{
marcadores[e.lado].sumaPuntuacion(1);
}

- marcadores[e.lado]: Tenemos dos marcadores dentro del Vector. Uno en la posición "0" y otro en la posición "1". ¿Cómo decidimos dónde sumar puntuación? Con el atributo antes mencionado.


Y ya tenemos los marcadores funcionando.








Anterior Índice Siguiente

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

Bug (I): La Bola loca

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


Bug (I): La bola loca

Los errores imprevistos (o bugs) son algo bastante común en la programación, y que suelen aparecer en la ejecución continuada de nuestros programas. Por mucho que se planee una implementación de código, siempre acaba apareciendo una situación inesperada que rompe nuestro preciado código. No pasa nada. Si aparece un bug se soluciona, y a otra cosa.


En este caso, nosotros encontramos un error si intentamos golpear la pelota con la partes puntiagudas (los extremos) del pong. No siempre sucede, pero la mayoría de las veces la bola empieza a rebotar dentro del pong. Queda atrapada.


En código, el problema viene dado porque la bola rebota continuadas veces en poco tiempo, cambiando su dirección de izquierda a derecha (y viceversa) una y otra vez sin llegar a salir del área de colisión del pong.


La solución que propongo es añadir un Timer a la bola. Un reloj que, tras la colisión, espere un tiempo para volver a comprobar si hay colisiones, y así dar tiempo a la bola para abandonar la zona de colisiones del pong.


En código, añadimos estos atributos a la clase Bola:


  // Para evitar múltiples colisiones
private var colisionado:Boolean = false;
private var timer:Timer = new Timer(100);

Añadimos la inicialización del timer al constructor:


  public function Bola(stage:Stage) 
{
stageRef = stage;
addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
iniciaPosicion();
timer.addEventListener(TimerEvent.TIMER, colisionTime, false, 0, true);
timer.stop();
}

Lo que decimos con este código, es que cada vez que se cumpla el tiempo especificado en el constructor del Timer se ejecute la función colisionTime. Además, lo detenemos con timer.stop(), porque de momento no queremos que corra. El código de colisionTime es el siguiente:


  public function colisionTime(e:TimerEvent)
{
timer.stop();
colisionado = false;
}

Es decir, pasado el tiempo, indicamos que la bola "ha dejado de colisionar". Pero nos falta, entonces, ver cuándo colisionado cambia a true. Este cambio sucede en la función colisiona:


  public function colisiona(objeto:IColisionable):void
{
if (!colisionado)
{
var vel_x:Number = Math.abs(vx);
if (vel_x < max_velocidad)
{
vel_x += acc;
}

if (vx > 0)
{
vx = -vel_x;
}
else
{
vx = vel_x;
}
vy = ((y - objeto.getY()) / objeto.getHeight() * 2) * vel_x;

colisionado = true;
timer.start();
}
}

Si no acaba de colisionar, dejamos que se produzcan las modificaciones propias de la colisión. Después ponemos colisionado a "true" e iniciamos el reloj, que, pasado el periodo marcado, volverá a dejar la bola activa para colisiones.


Sin embargo, si la bola acabara de colisionar, la función no se ejecutaría.


Si soy sincero, la solución que he encontrado no me acaba de convencer. Funciona, sí, pero quizá algo así debería tener una solución más sencilla. Si alguien tiene una solución mejor, que no dude en proponerla en los comentarios.








Anterior Índice Siguiente

El código del Escenario

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


La nueva clase Main

Como dijimos anteriormente, la clase Escenario será la encargada de controlar la lógica del juego. La clase Main ahora únicamente se encargará de crear el control de teclado, el Escenario y de añadirlo a Flash. La clase Main presupone (y presupone bien) que la clase Escenario se encargará de todo. Así pues, esta es nuestra nueva clase Main, que no tiene mayor misterio:


package
{
import flash.display.MovieClip;
import mi.pong.escenario.Escenario;
import com.senocular.utils.KeyObject;

/**
* ...
* @author ASL
*/
public class Main extends MovieClip
{
private var key:KeyObject;

public function Main()
{
key = new KeyObject(stage);
var escenario:Escenario = new Escenario(stage, key);
stage.addChildAt(escenario, 0);
}

}

}

El código del Escenario

Para empezar, necesitamos añadir nuevos atributos en la clase Escenario para soportar la nueva lógica:


  // Objetos estáticos sobre los que se puede colisionar, incluidos los pongs
protected var colisionables:Vector.<IColisionable>;

// Objetos móviles que pueden colisionar con objetos estáticos o con otros
// objetos móviles
protected var colisionadores:Vector.<IColisionador>;

// Variables generales
protected var key:KeyObject;
protected var stageRef:Stage;

Y modificar el constructor del escenario:


(Por comodidad, no añado los imports del código, pero recuerda que todas las clases que utilices deben estar importadas, si no, el código no compilará)


  public function Escenario(stage:Stage, keyObject:KeyObject) 
{
stageRef = stage;
key = keyObject;
colisionables = new Vector.<IColisionable>;
colisionadores = new Vector.<IColisionador>;
creaPongs(2);
creaEscenario();
creaBolas(1);
addEventListener(Event.ENTER_FRAME, bucle, false, 0, true);
}

Vemos que en el constructor recibimos el stage y el control de teclado keyObject, que asignamos a sus atributos correspondientes. Igualmente, creamos las listas de colisionables y colisionadores y después vienen una serie de funciones (aún no definidas) en las que inicializamos todo el escenario: creaPongs, creaEscenario, creaBola.


Finalmente, añadimos al escenario un EventListener que al inicio de cada FRAME ejecutará la función bucle (también sin definir). Esta función será el bucle del juego.


Puede parecer que estamos haciendo muchas cosas y todas ellas muy complejas, pero no es así. Repasemos: queremos transferir la lógica del Main al Escenario para lograr un mayor encapsulamiento. Así que hemos añadido los atributos necesarios para esta tarea al Escenario y los hemos inicializado. Hemos creado, también, los pongs, hemos dibujado el escenario y creado una bola (aunque aún no hayamos visto el código concreto de nada de esto). Y además, iniciado el bucle del juego, que se encarga de toda la lógica.


Veamos ahora el código concreto de cuada una de estas funciones no definidas:


creaPongs

  /**
* Añade un número de pongs al escenario.
* @param num_pongs Número de pongs a crear.
*/
public function creaPongs(num_pongs:uint)
{
for (var i:uint = 0; i < num_pongs; i++)
{
var miPong:Pong = new Pong(stageRef, i, key);
stageRef.addChild(miPong);
colisionables.push(miPong);
}
}

Un bucle en el que creamos un pong por vuelta. Y además, lo añadimos al escenario y a la lista de colisionables. Clave este último detalle.


El código entre en /** ... */ es documentación del código. En @param estamos especificando que la función recibe como parámetro "num_pongs". Existen herramientas que pueden traducirnos toda esta información en una web, al estilo de la documentación de ActionScript 3. Pero este es un tema que, de momento, no se va a cubrir en esta guía.


creaBolas

  /**
* Añade un número de bolas al escenario.
* @param num_bolas Número de bolas a crear.
*/
public function creaBolas(num_bolas:uint)
{
for (var i:uint = 0; i < num_bolas; i++)
{
var bola:Bola = new Bola(stageRef);
stageRef.addChild(bola);
colisionadores.push(bola);
}
}

Análogo (para variar) a la creación de pongs. La única diferencia, que la bola, como corresponde, se añade a la lista de colisionadores.


creaEscenario

  /**
* Crea el escenario. Un cuadrado verde que ocupa todo el stage.
*/
public function creaEscenario()
{
graphics.beginFill(0x66FF99);
graphics.drawRect(0, 0, stageRef.stageWidth, stageRef.stageHeight);
graphics.endFill();
}

La apariencia del Escenario por defecto es sencilla: un cuadrado verde que ocupa todo la pantalla Flash.


En esta función, hacemos uso de las funciones que ofrece ActionScript 3 para dibujar directamente sobre la pantalla Flash. Aunque parece complejo, en verdad es bastante sencillo. El único pero es que hay que seguir una metodología bastante marcada para que todo funcione correctamente. Algunas claves (que ampliaremos en los siguientes capítulos):


1) Las funciones de dibujo se invocan siempre desde el atributo graphics (cuya clase es Graphics) que contiene la clase Sprite y sus herederas. MovieClip entre ellas.


2) beginFill(color): indicamos que vamos a comenzar a dibujar formas del color que especificamos en el parámetro color.


3) drawRect(x, y, width, height): Dibujamos un rectángulo que parte en el punto x e y cuyo ancho mide width y cuyo alto mide height.


4) endFill(): indicamos que hemos terminado de dibujar.


Importante: Todas las instrucciones de dibujado de formas deben estar obligatoriamente entre beginFill y endFill, si no, obtendremos resultados inesperados (A aquéllos que hayan trabajado con OpenGL esto les sonará).


bucle

  /**
* Bucle principal del escenario.
* @param e Evento que produjo el disparo de la función.
*/
private function bucle(e:Event)
{
for (var i = 0; i < colisionadores.length; i++)
{
for (var j = 0; j < colisionables.length; j++)
{
if (colisionadores[i].getHitBox().hitTestObject(colisionables[j].getHitBox()))
{
colisionadores[i].colisiona(colisionables[j]);
colisionables[j].recibeColision(colisionadores[i]);
}
}
}
}

Tenemos dos bucles anidados, en los que estamos recorriendo la lista de colisionadores y comprobando con cada uno de los objetos de la lista colisionables si se produjo alguna colisión. Si así fuere, llamaríamos a las funciones correspondientes: colisionadores[i].colisionaba(colisionable[j]) y colisionables[j].recibeColision(colisionadores[i]).


En verdad, como de momento sólo tenemos bolas y pongs, esto se podría traducir por bola.colisiona(pong) y pong.recibeColision(bola).


La bola que colisiona...

Pues ya estamos preparados para implementar la funcion colisiona en la clase Bola que dejamos vacía en el capítulo anterior. Pero antes, vamos a a modificar un par de casas en la clase Bola:


1) En los atributos. Esto son todos los atributos que tiene, por el momento, Bola:


  private var vx:Number = 5;
private var vy:Number = 0;
private var max_velocidad:Number = 15;
private var acc:Number = 0.25;

private var stageRef:Stage;

Como se puede ver, aparece acc, que es la aceleración de la bola, que sumaremos cada vez que colisione. Y desaparece el atributo colisionadores, ya que es ahora el escenario quien se encarga de las colisiones de la bola. No olvides eliminar también el bucle en el que se testean las colisiones en la función loop. En consecuencia, éste es el nuevo constructor de Bola:


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

Ahora sí, el código de colisiona. Recuerda que esta función se ejecutará siempre que la bola colisione:


public function colisiona(objeto:IColisionable):void
{
var vel_x:Number = Math.abs(vx);
if (vel_x < max_velocidad)
{
vel_x += acc;
}
if (vx > 0)
{
vx = -vel_x;
}
else
{
vx = vel_x;
}
vy = ((y - objeto.getY()) / objeto.getHeight() * 2) * vel_x;

}

En esencia, estamos cambiando la dirección de la bola y aumentando su aceleración. Siempre que su velocidad no supere la máxima permitida.


Un detalle importante, es que no podemos poner max_velocidad el valor que deseemos. Si la ponemos demasiado alta, la bola atravesará al pong. En el futuro intentaremos solucionar este problema.








Anterior Índice Siguiente

La Bola y el Pong como IColisionador e IColisionable

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


Un pequeño problema...

Antes de nada vamos a realizar algo de trabajo sucio y molesto. En nuestro afán de mantener nuestro código ordenado, no serán pocas las veces que tengamos que reescribir y cambiar cosas que ya teníamos, como es el caso que se nos presenta a continuación.


Queremos una organización como la de la imagen. Así que vamos a crear las carpetas que nos falten y mover los .as que tenemos a su lugar correspondiente. Además, en las carpetas colisionable y colisionador vamos a clickar con el boton derecho, Add... y en esta ocasión, como lo que queremos son dos nuevas interfaces -> New Interface... Lo haremos por dos veces, con los nombres IColisionador e IColisionable en sus respectivas carpetas.




Y, aunque no lo pareciese, con este simple movimiento nos hemos cargado varias cosas que antes funcionaban. Abre ahora Bola.as y la clase Pong.as y verás dos mensajes advirtiéndote que el nombre de paquete no concucerda con la ruta de carpetas. Y no sólo eso, también en el fichero Flash, si nos vamos a las propiedades del Clip de Película Bola o de Pong de la bibloteca, si en el apartado Clase clickamos en el tic que valida la clase, también obtendremos un error. ¿Qué ha pasado? Pues que hemos movido los archivos de carpeta, y ahora Flash nos lo encuentra.


La solución es sencilla: en "Pong.as" cambiamos la actual cabecera del paquete por esto: package mi.pong.objeto.colisionable (la nueva ruta de carpetas en la que se encuentra Pong) y en las propiedades del símbolo Flash cambiamos la clase por su nueva localización mi.pong.objeto.colisionable.Pong. Repetimos proceso (sólo que cambiando "colisionable" por "colisionador") con la clase Bola.


En el futuro tendremos cuidado para evitar estas pérdidas de tiempo.


IColisionable e IColisionador

Definimos en sendos archivos las interfaces IColisionable


package mi.pong.objeto.colisionable 
{
import flash.display.DisplayObject;
import mi.pong.objeto.colisionador.IColisionador;

/**
* ...
* @author ASL
*/
public interface IColisionable
{
function getHitBox():DisplayObject;
function recibeColision(objeto:IColisionador):void;
function getY():Number;
function getHeight():Number;
}

}

e IColisionador


package mi.pong.objeto.colisionador 
{
import flash.display.DisplayObject;
import mi.pong.objeto.colisionable.IColisionable;

/**
* ...
* @author ASL
*/
public interface IColisionador
{
function getHitBox():DisplayObject;
function colisiona(objeto:IColisionable):void;
}

}

Vamos por partes:


- function getHitBox():DisplayObject; Esta función, definida en ambas interfaces, será la que proporciene el área de colisión de cada objeto. El rectángulo transparente al que llamamos "hitBox" y que añadimos en los símbolos Bola y Pong de nuestro Flash en capítulos anteriores. El objeto que se devuelve es de tipo DisplayObject, que es una clase antecesora (el padre del padre del padre...) de MovieClip. ¿Por qué DisplayObject y no MovieClip? Por generalidad. DisplayObject representa cualquier objeto que pueda representarse en el canvas de Flash. MovieClip representa un único tipo de objetos que pueden representarse por pantalla.


- function recibeColision( objeto:IColisionador ) y function colisiona( objeto:IColisionable ) : es la función que se ejecutará cuándo el objeto reciba la colisión / colisione. Además, le pasaremos como parámetro aquello contra lo que colisionó.


- function getY():Number; function getHeight():Number; Sólo las hemos puesto (de momento) en IColisionable. Como sus nombres indican, nos devuelven la posición en el eje "y" y la altura del pong. Las necesitaremos para calcular la trayectoria de la bola.


Muy bien, tenemos nuestras dos interfaces. ¿Y? ¿Qué tienen que ver con Bola y Pong? Bueno, de momento nada.


Antes de continuar, recordemos que la idea era que el Escenario tratara los objetos que contuviere de manera genérica, sin preocuparse de si eran bolas, pongs, ítems... Sólo necesitaba saber si eran IColisionable o IColisionador. Pues el siguiente paso es convertir las clases que tenemos de momento (Bola y Pong) en ICoisionable o IColisonador. Y eso lo conseguimos implementando las respectivas interfaces.


Bola como IColsionador

La bola es un colisionador. Para decírselo a ActionScript 3, debemos especificarlo en Bola.as.


En la zona de importación, importamos la interfaz deseada. En verdad, no es realmente necesaria esta importación, porque, al encontrarse en la misma carpeta, Bola "ve" a IColisionador. La que sí es necesaria es IColisionable, debido a colisiona(objeto:IColisionable):


import mi.pong.objeto.colisionador.IColisionador;
import mi.pong.objeto.colisionable.IColisionable;

Ahora, en la definición de clase, especificamos que Bola implementa IColisionable:


public class Bola extends MovieClip implements IColisionador 
{
...

Y por último, implementamos los métodos de la interfaz en la clase Bola. Hay que implementarlos todos, si no obtendríamos un error. Añadimos las definiciones al final de la clase:


  // Implementación de la interfaz IColisionable
public function getHitBox():DisplayObject
{
return hitBox;
}

public function colisiona(objeto:IColisionable):void
{

}

- En getHitBox() devolvemos hitBox que es el área de colisión definida en forma de rectángulo en Flash. Vamos, que estamos devolviendo la cajita transparente que tiene la Bola. La necesitaremos para calcular colisiones.


- De momento, dejamos sin definir colsiona(objeto:IColisionable). Lo haremos cuándo tengamos establecido el flujo de control del escenario.


Pong como IColisionable

Los pasos a seguir en este caso son análogos a los seguidos con Bola:



import mi.pong.objeto.colisionador.IColisionador;
import mi.pong.objeto.colisionable.IColisionable;

public class Pong extends MovieClip implements IColisionable
{
...

  // Interfaz IColisionable
public function getHitBox():DisplayObject
{
return hitBox;
}

public function recibeColision(objeto:IColisionador):void
{

}

public function getY():Number
{
return y;
}

public function getHeight():Number
{
return height;
}

- En getHitBox() devolvemos igualmente hitBox y en getY() y getHeight() la posición "y" y la altura del objeto respectivamente.


Para cerrar el ciclo y e intentar aclarar un poco las ideas respecto a las interfaces, vayámonos a la clase Escenario, y añadamos el siguiente código:


package mi.pong.escenario 
{
import flash.display.MovieClip;
import mi.pong.objeto.colisionable.IColisionable;
import mi.pong.objeto.colisionador.IColisionador;

/**
* @author ASL
*/
public class Escenario extends MovieClip
{
// Objetos estáticos sobre los que se puede colisionar, incluidos los pongs
protected var colisionables:Vector.<IColisionable>;

// Objetos móviles que pueden colisionar con objetos estáticos o con otros
// objetos móviles
protected var colisionadores:Vector.<IColisionador>;

public function Escenario()
{

}
}
}

Lo más importante es protected var colisionables:Vector.<IColisionable> y protected var colisionadores:Vector.<IColisionador>.


- Primero, y volviendo a la POO, protected indica que el atributo es solamente visible por las clases herederas y por la propia clase..


- Y segundo, aquí vemos por fin el uso de las interfaces. Especificamos dos listas (Vector), una que contiene objetos de tipo IColisionable y la otra de tipo IColisionador. Y el escenario no sabe si son bolas, pongs, ítems... Sólo sabe que unas colisionan y otras son colisionables. Y las trata a todas por igual.


Un último detalle, para que Flash reconozca la clase Vector, debéis comprobar dos cosas:


1) En FlashDevelop, en Project -> Properties -> Output -> Target debe estar seleccionado Flash Player 10.


2) En Flash CS4, en Archivo -> Configuración de la Publicación -> en la pestaña Flash -> Reproductor y debe poner igualmente Flash Player 10.


Si utilizas versiones de Flash anteriores y no dispones de la clase Vector puedes utilizar perfectamente en su lugar en la clase Array.








Anterior Índice Siguiente

¿Qué queremos que haga nuestro Escenario?

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


Y sí, lamentablemente y por falta de tiempo, tuve que prescindir de las imágenes para las cabeceras de cada capítulo.


Definición del Escenario

En previsión de lo que va a ser un aumento exponencial de las clases de nuestro juego, deberíamos comenzar a organizarlas de manera estructurada para que nuestro proyecto Flash no se acabe convirtiendo en una amalgama de código intratable.


Todo lo relacionado con los escenarios parece tener la suficiente importancia como para poder considerarlo como un bloque separado de Pong y Bola. Así, en nuestro proyecto de FlashDevelop, dentro de la carpeta "pong", vamos a añadir una subcarpeta (un nuevo paquete) a la que llamaremos "escenario". Y dentro de esta carpeta, creamos nuestra clase Escenario.


Antes de escribir código, vamos a pensar que representa esta clase Escenario y qué queremos hacer con ella.


Ya nombramos el encapsulamiento de la Programación Orientada a Objetos. Pero vamos a definirlo bien para que no nos perdamos.


¿Qué es el encapsulamiento?

En POO, es el proceso por el que ocultamos la implementación interna de una clase (o concepto) y sólo podemos acceder y modificar su estado por métodos definidos por la propia clase. Es decir, sabemos como interactuar con él y lo que podemos esperar que haga, pero no sabemos "cómo funciona por dentro".


Lo que queremos es encapsular el Escenario (bueno, el Escenario y muchas cosas más...) y que sea él quién se encargue de colocar los pongs, de controlar las colisiones y los marcadores y de otras muchas cosas que ya veremos. Y queremos que lo haga bien. Y queremos que desde cualquier otro sitio (la clase Main, por ahora), cuándo un objeto de la clase Escenario sea creado todo funcione correctamente. Creamos el Escenario y nos olvidamos.


Bien, pues pensemos qué tareas tendrá asignada nuestra maravillosa clase Escenario:


- Creación y colocación de los pongs, bolas y otros objetos.


- Control de colisiones.


- Control de puntuación.


Bueno, esto de momento.


Control de colisiones: Colisionadores y colisionables

Hasta ahora, el control de colisiones había sido muy sencillo. Le decíamos a la bola con que cosas podía colisionar y ella se encargaba de ver si chocaba o no. Esta información se le transmitíamos a la bola en su creación. Pero hacerlo así tiene una consecuencia: el número de objetos con los que la bola podía colsionar no puede aumentar ni disminuir. Y, aunque de momento esto no sucede, sucederá. Y en previsión de ello, en nuestro escenario vamos a definir dos tipos de objetos:


- Colisionadores: aquellos objetos que pueden lanzarse a la búsqueda del choque de otros objetos. Ej: la bola, misiles lanzados desde los pongs, etc.


- Colisionables: objetos que esperan ser embestidos por los objetos colisionadores. Ej: los pongs, los ítems que aparezcan en el escenario, etc.


Y ahora llega el gran problema: ¿cómo le decimos a ActionScript 3 que un pong es un colisionable y una bola un colisionador? Una solución factible podría ser definir dos clases: Colisionadores y Colisionables, y que cada una de las clases necesarias heredase de su respectiva clase padre.


Lamentablemente, esta solución no es viable debido a que ActionScript 3 no soporta la herencia múltiple (heredar de varias clases simultáneamente) como por ejemplo C++. Sin embargo existe una solución parecida, que nos servirá perfectamente para la realización de nuestro propósito: las interfaces.


¿Qué es una interfaz?

Simplificando mucho, una interfaz es un conjunto de métodos sin cuerpo (sin definición). Se dice que una clase implementa una interfaz cuándo define todas sus funciones.


Para concretizar un poco más, utilicemos un ejemplo. Por ejemplo, supongamos que tenemos una interfaz Volador. Y esta interfaz define los siguientes métodos: despegar, volar y aterrizar.


Y ahora tenemos dos clases: Pájaro y Avión. Nuestro propósito es crear una clase Cielo que contenga todo tipo de objetos voladores, pero a la clase Cielo no le interesa saber si esos objetos son pájaros o aviones. Sólo que son voladores. Y quiere tratarlos a todos de la misma manera. Por ello tendrá un conjunto de objetos que implementan la interfaz Volador.


Así, Pájaro tendrá sus propios métodos y atributos: comer, limpiarse, y además implementará la interfaz Volador, definiendo despegar, volar y aterrizar. Lo mismo sucederá con Avión, que tendrá sus métodos y atributos propios, pero que también definirá despegar, volar y aterrizar.


Y es importante destacar que la implmentación de despegar en Pájaro es distinta que la de Avión. Un pájaro despega de manera distinta a cómo lo hace un avión. Sí. Pero los dos pueden despegar, porque los dos son voladores. En este punto es dónde radica la potencia de las interfaces.


Definición de interfaces en ActionScript 3

public interface INombre
{
function metodo1([parámetros1]):tipo_devol1;
function metdoo2([parámetros2]):tipo_devol2;
...
}

Las interfaces no admiten atributos. Por una convención no escrita (o quizá sí) todas los nombres de nuestras interfaces comenzarán con "I", por aquello de Interfaz.


Pasemos, por fin, a escribir el código para nuestro escenario y vemos mejor, también, todo esto de las interfaces.








Anterior Índice Siguiente

FASE II: Objetivos

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



Resultado al finalizar la Fase II

Objetivos

- Creación de un escenario de fondo.


- Introducción de marcadores de puntuación.


- Arreglar algunos bugs.

Para realizar estos cambios vamos a introducir un poco (pero muy poco) de Ingeniería del Software, estructurando el código de nuestro juego de manera que en el futuro nos resulte lo más sencillo posible introducir cambios.


En la Programación Orientada a Objetos son comunes los diagramas UML. Esquemas gráficos en los que podemos entender de un vistazo como está estrucutrada nuestra aplicación. El diagrama UML (simplificado) que define los objetivos que nos hemos marcado en esta fase es:



No vamos a hacer un estudio en profundidad en UML, pero sí a definir las bases.


- Las clases se definen dentro de cajas. Así, en el diagrama aparecen las clases que ya teníamos en el pasado (Main, Pong, Bola) y algunas nuevas (Escenario, CampoFutbol, Marcador).


- Las agregaciones se definen con un diamente conectado a las componentes de la agregación. En Escenario, por ejemplo, tenemos un diamente al que se conectan Marcador, Pong y Bola. Esto significa que, entre otras cosas, el Escenario se compondrá de marcadores, pongs y bolas (sin especificar el número).


- Las generalizaciones (o herencia) se define con una flecha que va desde la clase más concreta a la clase más general. En esencia, CampoFutbol es una clase hija de Escenario. Hereda todas sus características y además añadirá otras propias de un campo de fútbol.


Echando un vistazo al diagrama UML podemos sacar en claro que ahora será la clase Escenario la que se encargue de interactuar con bolas, pongs y marcadores, y la clase Main, simplmente, se encargará de crear un Escenario, sin preocuparse de cómo funciona por dentro. Está separación (o encapsulación) nos será muy útil, por ejemplo, a la hora de meter un menú o una pantalla de inicio en nuestro juego. Bastará con que la clase Main llame al Menú, y el Menú al Escenario.


CampoFutbol será uno de los primeros escenarios que creemos para nuestro Pong.








Anterior Índice Siguiente

Integrar JavaScript y XML: un ejemplo sencillo, pero potente

Vamos a mostrar, a través de un ejemplo, la sencilla manera en la que podemos integrar JavaScript y XML.


Supongamos que queremos incluir en nuestra web un apartado que contenga una lista de hipervínculos (o links). El problema es que esta lista va a variar continuamente y no queremos andar modificando el HTML cada vez.


La solución consiste en tener la lista de hipervínculos en un archivo XML externo y cargar la lista dinámicamente ayudados de JavaScript y su parser XML.


Nuestro archivo XML tiene el siguiente formato:


<?xml version="1.0" encoding="iso-8859-1"?>
<lista>
<link>
<titulo>Google</titulo>
<href>http://www.google.es</href>
</link>
<link>
<titulo>Metafísica de Costumbres</titulo>
<href>http://metafisicadecostumbres.blogspot.com</href>
</link>
<link>
<titulo>Metafísica Informática</titulo>
<href>http://metafisicainformatica.blogspot.com</href>
</link>
</lista>

Un detalle importante es la cabecera XML. Necesitamos especificar encoding="iso-8859-1" para evitar problemas con las tildes y las ñ. De no hacerlo, nos aparecerían símbolos raros en lugar de ñ y vocales acentuadas.


El código JavaScript que necesitamos para resolver nuestro problema es el siguiente:


<html>
<body>
<div id="links"></div>
<script type="text/javascript">
// En la variable div_links obtenemos el contenedor div con el id 'links'
var div_links = document.getElementById('links');

var xmlDoc = cargarXMLDoc('links.xml');
if (xmlDoc != null)
{

// Obtenemos la lista de links
var links_tag = xmlDoc.getElementsByTagName("lista")[0].getElementsByTagName("link");

for (var i = 0; i < links_tag.length; i++)
{
// Obtenemos el título del link
var titulo = links_tag[i].getElementsByTagName("titulo")[0].childNodes[0].nodeValue;

// Obtenemos el hipervínculo del link
var href = links_tag[i].getElementsByTagName("href")[0].childNodes[0].nodeValue;

// Modificamos el contenido html del contenedor div
div_links.innerHTML += "<p><a href=" + href + ">" + titulo + "</a></p>";
}
}

function cargarXMLDoc(archivoXML)
{
var xmlDoc;

if (window.XMLHttpRequest)
{
xmlDoc = new window.XMLHttpRequest();
xmlDoc.open("GET", archivoXML, false);
xmlDoc.send("");
return xmlDoc.responseXML;
}
// para IE 5 y IE 6
else if (ActiveXObject("Microsoft.XMLDOM"))
{
xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async = false;
xmlDoc.load(archivoXML);
return xmlDoc;
}
alert("Error cargando el documento.");
return null;
}
</script>
</body>
</html>

Puedes encontrar información detallada sobre el parser XML (en inglés) aquí.


Fase I: Conclusión

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


Fase I: Conclusión

Hemos llegado al final de la Fase I.


Puedes descargar aquí los archivos fuente del proyecto.


También me gustaría utilizar este post a modo de feedback, y que en los comentarios dejarais vuestra opinión sobre lo que llevamos de guía. Sugerencias de mejora, si hay cosas poco (o mal) explicadas, fallos, e incluso vuestras propias versiones del juego.


A partir de aquí, puedes continuar con la Fase II de esta guía, en la que introduciremos una serie de mejoras.








Anterior Índice Siguiente

Tutorial ActionScript 3 - Juego Pong - Principiantes

ActionScript 3.0 - Guía para principiantes


Objetivo

Click en la ventana Flash para jugar. Controles: w, s, flecha arriba, flecha abajo.



¿De qué va la Guía?

Bienvenidos al primer curso de esta Metafísica Informática: ActionScript 3.0 - Guía para principiantes.


ActionScript 3.0 es un poderosísimo lenguaje de script integrado en Flash y que permite realizar todo tipo de virguerías, desde películas de animación hasta juegos realmente complejos.


¿Cuál es el objetivo?

El enfoque de la guía será doble:


  • Teórico: Aprender y entender los pormenores de ActionScript 3.0 y los conceptos básicos (y no tan básicos) de la Programación Orientadas a Objetos.

  • Práctico: Aplicar la teoría en un proyecto práctico: El desarrollo desde cero del clásico juego Pong. Un juego, en esencia, bastante simple (en su desarrollo y juego), pero al que iremos añadiendo difrentes items y funcionalidades para darle mayor gracia.


¿Qué necesito saber para seguirla?

No vendría mal algo de experiencia con la parte gráfica de Flash: trabajo con capas, formas, colores, dregadados... Aunque si no es el caso, no importa demasiado. Cuándo utilicemos Flash para desarrollar la parte visual de nuestro Pong, se procurará explicar detalladamente todos los pasos.


Respecto a conocimientos de programación, lo básico: Qué es una variable, un bucle, una condición...


No es necesario ningún conocimiento sobre Programación Orientada a Objetos.


Últimas indicaciones

No necesitas haber trabajado con la versión antigua de ActionScript, ActionScript 2. Aunque comparten puntos comunes, ActionScript 2 y ActionScript 3 son lenguajes que trabajan de manera distinta.


La guía se va a realizar sobre Flash CS4, aunque en principio puede seguirse con cualquier otra versión de Flash que admita ActionScript 3.


Algo importante que quiero recalcar es que esta guía está escrita de aprendiz a aprendiz. Mi trabajo con ActionScript 3 y Flash es relativamente reciente, y cualquier comentario o corrección será bien recibido.



Puedes comenzar la guía de ActionScript 3, o echar un vistazo al índice que viene a continuación.
Índice




  1. Primeros pasos

    1. Lo primero, organización

    2. Introducción. FlashDevelop: organizando nuestros archivos .as. Jerarquía de carpetas

    3. La clase del Documento

    4. ¿Qué es la clase del Documento? Creando la clase del documento. Comentarios en AS3. Conectando nuestro Flash con Main.as.


  2. ActionScript y la Programación Orientada a Objetos.

    1. Concepto de clase. Clases en ActionScript 3

    2. Un mundo lleno de objetos... Concepto de Clase. ¿Qué es un atributo? ¿Qué es un método? ¿Cuál es la diferencia entre Clase y Objeto? Clases en ActionScript 3. Definición de paquetes. Definición de clases. Añadiendo atributos y métodos a nuestra clase Pong. Definición de variables. Visibilidad de atributos y funciones. Definición de atributos. Definición de funciones. El constructor de la clase. Creando un objeto de la clase Pong.

    3. Herencia en ActionScript 3

    4. Diseño gráfico de nuestro pong. Enlazando la clase Pong con su representación gráfica. Añadiendo el pong a la escena. ¿Cuál es la idea básica tras la herencia?


  3. Finalizando nuestro Pong básico

    1. Controlando el pong desde teclado

    2. Colocando los pongs. ¿Cómo accedo a los métodos y atributos de un objeto? Controlando desde el teclado nuestros pongs. ¿Qué es un evento?

    3. Movimiento avanzado

    4. Control de velocidad realista. Acotando los límites del movimiento.

    5. Detección de colisiones

    6. La clase Bola. Detección de colisiones. ¿Cómo detecto colisiones en ActionScript?. Toques finales.


  4. Conclusión y archivos fuente Pong 1.0



FASE II: Pong 1.1 - Pong con escenarios y marcadores



  1. Objetivos

  2. Creando el Escenario

    1. ¿Qué queremos que haga nuestro escenario?

    2. Definición del Escenario. ¿Qué es el encapsulamiento? Control de colisiones: Colisionadores y colisionables. ¿Qué es una interfaz? Definición de interfaces en ActionScript 3.

    3. La Bola y el Pong como IColisionador e IColisionable

    4. Un pequeño problema... IColisionable e IColisionador. Bola como IColisionador. Pong como IColisionable.

    5. El código del Escenario
      La nueva clase Main. El código del Escenario. creaPongs, creaBolas, creaEscenario, bucle. La bola que colisiona...



  3. Bug (I): La Bola loca

  4. Añadiendo los marcadores

    1. Produciendo y detectando los goles

    2. ¡Gol! ¿Qué es un evento? Definición de GolEvent. ¿Qué es un atributo estático (static) o de clase? ¿Para qué sirve la palabra clave "super"? ¿Quién produce el GolEvent? ¿Quién detecta el GolEvent?

    3. Añadiendo los marcadores

    4. Nuestro marcador Flash. Código del marcador. Añadiendo los marcadores al Escenario

    5. Un campo de fútbol

    6. La clase CampoFutbol


  5. Conclusiones y archivos fuente del Pong 1.1


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

3. II. Movimiento avanzado

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



Control de velocidad realista

Vamos a realizar un control de velocidad basado en términos de aceleración. Es decir, cada vez que pulsemos una tecla, vamos a propulsar nuestro pong, sumándole una aceleración la velocidad que ya tuviere. Además, para que la cosa no se vaya de madre y el pong se vuelva medio loco, vamos a simular fricción con el aire, que frenará nuestro pong. Nuestras variables para el control de velocidad serán:


  // Control de velocidad
private var vy:Number = 0; // Velocidad actual
private var acc:Number = 1; // Aceleración
private var friccion:Number = 0.95; // Fricción con el aire

Inicializar los atributos en su declaración surge el mismo efecto que si los iniciaramos en el constructor de clase.


Para hacer uso de estas nuevos atributos, modificamos la función mover:


  public function mover(e:Event)
{
if (key.isDown(upKey))
{
vy -= acc;
}
else if (key.isDown(downKey))
{
vy += acc;
}

y += vy;
vy *= friccion;
}

Si probamos la película, veremos que nuestros pongs ahora se desplazan de manera más suave y realista.


Acotando los límites del movimiento

Aún así, seguimos teniendo un problema importante: Cuándo el pong llega a alguno de los límites de película, desaparece. Podemos controlar esto de manera sencilla, añadiendo el siguiente bloque al final de la función mover:


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

Con este trozo de código, controlamos que la posición "y" de nuestro pong (su punto central) no salga de los límites de la película (Recuerda que stageRef era una referencia al escenario principal de la película). Si el pong intenta salir de los límites, restablecemos su posición al límite más cercano y revertimos la dirección de su velocidad, consiguiendo un efecto de rebote bastante resultón.








Anterior Índice Siguiente

3.I. Controlando el pong desde teclado

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


Controlando desde el teclado nuestro pong


Dejamos atrás la POO y nos centramos en el desarrollo puro y duro de nuestro juego Flash.


Colocando los pongs

Para que nuestro juego tenga algo de sentido, necesitamos al menos dos pongs colocados en el campo de juego, uno al lado izquierdo y el oponente en el lado derecho. Para controlar su posición en el escenario vamos a utilizar los atributos "x" e "y" que nuestra clase Pong ha heredado de MovieClip.


¿Cómo accedo a los métodos y atributos de un objeto?

Utilizamos el punto "." para acceder a los miembros de un objeto.



// Acceso a atributos
miObjeto.atributo = valorAtributo;
// Acceso a métodos
miObjeto.cualquierMetodo(parametros);

Para simplificar la colocación de los pongs en el campo de juego, vamos a hacer a nuestra clase Pong "inteligente", añadiéndole un atributo "num_jugador" que contendrá el número jugador. Éste indicará si el jugador debe situarse a la izquierda (jugador 0) o a la derecha (jugador 1). Añadimos a la clase Pong el atributo:



...
private var num_jugador:uint;
...

- uint es el tipo de variable que engloba todos los números >= 0.


Para el cálculo de las coordenadas que ha de ocupar el pong, necesitamos saber el tamaño del escenario. ¿Quién tiene esta información? La clase Main, por supuesto. La clase Main ha heredado de MovieClip un atributo "stage" de la clase "Stage" que contiene las dimensiones del escenario.


stage


De hecho, estos valores coincidirán con los valores que especifices en las Propiedades del Documento (click derecho sobre el fondo -> Propiedades del Documento...)


Propiedades del documento


La ventaja de especificar la colocación de los pongs respecto a las dimensiones del escenario es que, si éstas cambian, todo permanecerá colocado en su sitio. Porque en el futuro será útil, vamos a añadir a la clase Pong un atributo "stageRef" de tipo "Stage" que contendrá los datos del escenario. Recuerda que has de importar Stage para evitar fallos en la compilación (import flash.display.Stage;):



...
private var num_jugador:uint;
private var stageRef:Stage;
...

Vamos a hacer saber a la clase Pong de su número de jugador y del escenario a través del constructor de la clase, añadiéndole parámetros. Borramos el viejo constructor y añadimos el siguiente:


  public function Pong(stage:Stage, n_jugador:uint) 
{
this.stageRef = stage;
this.num_jugador = n_jugador;
iniciaPosicion();
}

- stage:Stage, n_jugador:uint son los parámetros del constructor, los cuáles enviaremos cuándo creemos el objeto con new.


- this: Es una referencia al objeto actual. this da acceso a todos los miembros del objeto actual. En este caso, a los atributos stageRef y num_jugador de la clase Pong.


Cómo ves, tras asignar los atributos llamamos a la función iniciaPosicion() que tiene la siguiente especificación:


  public function iniciaPosicion()
{
var proporcion:uint = 20;
this.y = stageRef.stageHeight / 2;
if (num_jugador == 0)
{
this.x = stageRef.stageWidth / proporcion;
this.scaleX = -1;
}
else
this.x = stageRef.stageWidth - stageRef.stageWidth / proporcion;
}

Hemos utilizado algo de matemática sencilla para colocar los pongs en su lugar correspondiente. Con "this.scaleX = -1;", obtenemos la imagen espejo de nuestro pong, cosiguiendo así su orientación correcta en la pantalla. (Puedes probar ejecutar el Flash con "this.scaleX = -1" añadido y sin añadir para entender a lo que me refiero.).


La clase Pong queda tal que así:


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

/**
* ...
* @author ASL
*/

public class Pong extends MovieClip
{
private var vy:N
private var num_jugador:uint;
private var stageRef:Stage;

public function Pong(stage:Stage, n_jugador:uint) {
this.stageRef = stage;
this.num_jugador = n_jugador;
iniciaPosicion();
}

public function iniciaPosicion()
{
var proporcion:uint = 20;
this.y = stageRef.stageHeight / 2;
if (num_jugador == 0)
{
this.x = stageRef.stageWidth / proporcion;
this.scaleX = -1;
}
else
this.x = stageRef.stageWidth - stageRef.stageWidth / proporcion;
}
}
}

Y ya sólo queda añdir nuestros pongs desde la clase Main:


package
{
import flash.display.MovieClip;
import mi.pong.Pong;

/**
* ...
* @author ASL
*/
public class Main extends MovieClip
{

public function Main()
{
var miPong1:Pong = new Pong(stage, 0);
addChild(miPong1);

var miPong2:Pong = new Pong(stage, 1);
addChild(miPong2);
}

}

}

Recuerda que stage es un atributo dado por la clase Main (quién lo ha heredado de MovieClip) que representa el escenario del documento. Hubiera surtido el mismo efecto si hubiéramos utilizado "this.stage".


Controlando desde el teclado nuestros pongs

Para el control de teclado vamos a utilizar una clase ya creada de un señor que sabe bastante de ActionScript, Senocular. Puedes descargar la clase aquí. Deberías crear la jerarquía de carpetas com -> senocular -> utils y ahí crear el archivo KeyObject.as con el contenido del link.


KeyObject es una clase que nos va a permitir saber si una tecla cualquiera está pulsada en el teclado, y con esta información, mover el pong hacia un lado o hacia otro. Como tenemos un único teclado, sólo necsitamos un KeyObject para todo el juego, así que lo crearemos en la clase Main. Y además, ambos Pong necesitan saber de ése teclado, con lo cual añadiremos un parámetro más en el constructor de la clase Pong:


...

public class Main extends MovieClip
{
private var key:KeyObject;

public function Main()
{
key = new KeyObject(stage);

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

var miPong2:Pong = new Pong(stage, 1, key);
addChild(miPong2);
}

}

...

- key = new KeyObject(stage): El único parámetro que necesita KeyObject es el escenario principal.


Ahora hay que adaptar la clase Pong a las nuevas necesidades. Necesitamos un atributo key para guardar la información de teclado que nos pasan desde el constructor, y dos atributos más: upKey y downKey que especificarán las teclas concretas que mueven nuestro pong.


...
//Control de teclado
private var key:KeyObject;
private var upKey:uint;
private var downKey:uint;
...

Y necesitamos la inicialización de todas estas variables:


 ...
public function Pong(stage:Stage, n_jugador:uint, key:KeyObject)
{
this.stageRef = stage;
this.num_jugador = n_jugador;
this.key = key;

iniciaPosicion();
iniciaTeclado();

}
...


private function iniciaTeclado()
{
if (num_jugador == 1)
{
upKey = Keyboard.UP;
downKey = Keyboard.DOWN;
}
else
{
upKey = 87; // W
downKey = 83; // S
}
}

- Observa que declaramos iniciaTeclado() como private porque no queremos que nadie pueda acceder a ella desde fuera de la clase.


- upKey = Keyboard.UP; downKey = Keyboard.DOWN: Para el caso del jugador uno, asignamos a sus teclas las constantes para la tecla UP y DOWN que contiene Keyboard (import flash.ui.Keyboard;)


- En el caso del jugador 0 asignamos las constantes a mano porque, desgraciadamente y por algún motivo que desconozco, las constantes para las letras del telcado sólo existen en el AIR runtime, que no es nuestro caso. Puedes ver los códigos de teclas aquí. Ten cuidado también cuándo previsualices el juego desde Flash, porque estas teclas no te funcionarán. Para que lo hagan, ejecuta el archivo .swf que se crea en el mismo director que tu archivo .fla cada vez que previsualizas la película.


Ahora, lo que necesitamos es procesar las pulsaciones de estas teclas. Para ello vamos a utilizar la función mover y el atributo vy que declaramos hace tiempo:


  public function mover(e:Event)
{
if (key.isDown(upKey))
{
vy -= 1;
}
else if (key.isDown(downKey))
{
vy += 1;
}

this.y += vy;
}

- mover(e:Event): ahora explicaremos porque la función mover necesita este parámetro "e".


- key.isDown(una_tecla): esta función nos devuelve un booleano indicando si una_tecla está pulsada o no.


- this.y += vy : añadimos a la posición "y" del pong la velocidad, que ha sido modificada con las pulsaciones de teclado.


Todo está ya conectado, pero nos falta un último detalle: ¿cuándo se ejecuta la función mover? Para resolver este problema, añadimos la siguiente instrucción al constructor de la clase Pong:


...
addEventListener(Event.ENTER_FRAME, mover, false, 0, true);
...

- addEventListener (Event.ENTER_FRAME, mover, false, 0, true) : Con esta función le estamos diciendo a la clase Pong que cada vez que se produzca el evento "Event.ENTER_FRAME", ejecute la función "mover". El resto de parámetros de momento no tienen relevancia.


¿Qué es un evento?

En ActionScript, un evento es algo que sucede y que podemos detectar, para realizar las acciones oportunas. Los eventos pueden ser de múltiples tipos: eventos de teclado (se pulsó una tecla), eventos de ratón (se pulsó el botón derecho), eventos de película (se entró en un nuevo frame), etc.


En este caso, nos interesa que cada vez que nuestra película Flash entre en un nuevo Frame (fotograma) se actualicen las posiciones del pong.


La función mover(e:Event) necesita el parámetro "e" de tipo Event porque será ahí dónde se guarda el evento que se produjo. Sin este parámetro, no lograremos compilar.


Después de tanto trasiego, la clase Pong queda así:


package mi.pong 
{
import flash.display.MovieClip;
import flash.display.Stage;
import com.senocular.utils.KeyObject;
import flash.ui.Keyboard;
import flash.events.Event;

/**
* ...
* @author ASL
*/

public class Pong extends MovieClip
{
private var vy:Number;

private var num_jugador:uint;
private var stageRef:Stage;

//Control de teclado
private var key:KeyObject;
private var upKey:uint;
private var downKey:uint;

public function Pong(stage:Stage, n_jugador:uint, key:KeyObject)
{
this.stageRef = stage;
this.num_jugador = n_jugador;
this.key = key;

iniciaPosicion();
iniciaTeclado();


addEventListener(Event.ENTER_FRAME, mover, false, 0, true);

}

public function iniciaPosicion():void
{
var proporcion:uint = 20;
this.y = stageRef.stageHeight / 2;
if (num_jugador == 0)
{
this.x = stageRef.stageWidth / proporcion;
this.scaleX = -1;
}
else
this.x = stageRef.stageWidth - stageRef.stageWidth / proporcion;

vy = 0;
}

private function iniciaTeclado():void
{
if (num_jugador == 1)
{
upKey = Keyboard.UP;
downKey = Keyboard.DOWN;
}
else
{
upKey = 87; // W
downKey = 83; // S
}
}

public function mover(e:Event)
{
if (key.isDown(upKey))
{
vy -= 1;
}
else if (key.isDown(downKey))
{
vy += 1;
}

this.y += vy;
}
}
}

¡Los pongs ya están en movimiento! Aunque necesitamos un movimiento más fluido que el conseguido hasta ahora para que la jugabilidad sea satisfactoria. Tema que abordamos en el siguiente capítulo.








Anterior Índice Siguiente