Comunidad de diseño web y desarrollo en internet online

Creando el Pong en Flash (tutorial)

Citar            
MensajeEscrito el 07 Feb 2007 09:05 pm
Creando el PONG en Flash

Archivo zip
Ejemplo (.swf)

Hace no mucho, en el foro de Flash o ActionScript (ahora no me acuerdo) alguien posteó que estaba interesado en hacer un juego estilo Pong, pero que no lograba bien cambiar el ángulo de la pelota dependiendo de cuán cerca del centro de la paleta había ocurrido el impacto. Definitivamente, eso hacía al juego un tanto repetitivo.

El modo de mover la pelota fue lo que me interesó, porque hace unos años, había armado un juego similar al Arkanoid y lo había hecho de la misma manera.

Básicamente, había usado un código como el que posteo a continuación:

Código :

velX = velY = 5;
//
onEnterFrame = function () {
//
pelota._x += velX;
pelota._y += velY;
//
if (pelota.hitTest (cualquierCosaHorizontal) {
velY *= -1;
}
if (pelota.hitTest (cualquierCosaVertical) {
velX *= -1;
}
}


Bastante sencillo, pero quizá demasiado; lo que al cabo de un tiempo volvía al juego bastante aburrido y repetitivo.

Y ahora, armado con más conocimientos que los que tenía en ese momento, llegué a este nuevo código, bastante perfeccionado.



Bien, comencemos.

Qué es lo primero que hay que hacer? Si, algunos dirían el código, otros los dibujos, en este caso, dada la sencillez gráfica, vamos a elegir la segunda opción.

Armamos una paleta (ese va a ser el nombre con el que lo exportamos), una pelota (también exportada con ese nombre) y las paredes (simples rectángulos más anchos que altos, que vamos a exportar como paredes).

Vale aclarar que excepto la pared con su punto de anclaje en la esquina superior izquierda, todos los MCs están anclados en el centro.

Ahora, el código.

Código del Pong

Para comenzar, cargamos los clips de película en la pantalla y los ubicamos correctamente. Nada raro, cuanto más simple sea, mejor (es importante mencionar cuál es el tamaño de la película con la que trabajamos, en este caso, el estándar, 550 x 400 px. Si desean cambiarlo no olviden modificar también las posiciones iniciales para que todo quede centrado, así como todas las variables referentes a la posición. No uso Stage.height y Stage.width para evitar que al redimensionar la pantalla, todo se valla al demonio):

Código :

this.attachMovie ("Pared", "pared1", 1);
this.attachMovie ("Pared", "pared2", 2);
this.attachMovie ("Paleta", "pl1", 3);
this.attachMovie ("Paleta", "pl2", 4);
this.attachMovie ("Pelota", "pelota", 5);
//
pelota._y = 200;
pelota._x = 275;
//
pl1._y = pl2._y = 200;
pl1._x = 20;
pl2._x = 530;
pl1._rotation = pl2._rotation = 90;
//
pared1._x = pared2._x = 0;
pared1._y = 0;
pared2._y = 400 - pared2._height;


pl1 y pl2, son el jugador 1 y 2, respectivamente (no sé porqué no los llamo jug1 y jug2, pero para el caso es lo mismo).

Ahora bien, el jugador 2, va a estar controlado por la computadora y el 1 por el usuario.

Entonces, armamos el movimiento, como verán, vamos a hacerlo con easing para que quede más acabado, pero en una sola línea (si, sé contar, son 2, pero se puede hacer en una si sacamos la variable vel). Para obtener un juego más fluido, empleamos 30 fps, aunque este número puede ser modificado dependiendo de lo que querramos lograr, recordemos que cuantos más fps, más fluida será la animación.

Código :

onEnterFrame = function () {
vel = (pl1._y - _ymouse) / 10;
pl1._y -= vel;
}


Como se imaginarán, cambiando el número por el que dividimos la distancia cambiamos la aceleración. Pero por más alto que sea ese número, no vamos a poder controlar la velocidad de una manera muy exacta. Bien, entonces, hacemos esto, declaramos cuál va a ser esta velocidad y nos fijamos si la variable vel es mayor a ésta, en caso afirmativo, reemplazamos vel por la velocidad máxima que establecimos.


No hay que olvidar que, a veces, vel va a ser postiva y, a veces, negativa. Éste código lo toma en cuenta, así como toma en cuenta la existencia de las paredes que no deben poder atravesarse con las paletas (el código final no es exactamente este. Esto se debe a que el tutorial explica cómo crear un Pong en general y no exactamente el del ejemplo):

Código :

var vel, velocidad:Number;
var velocidadMax:Number = 10;
var velMax:Boolean;
//
//
onEnterFrame = function () {
vel = (pl1._y - _ymouse) / 10;
if (vel > velocidadMax || vel < -velocidadMax) {
if (_ymouse > pl1._y) {
velocidad = -velocidadMax;
} else {
velocidad = velocidadMax;
}
} else {
velocidad = vel;
}
pl1._y -= velocidad;
velMax = Math.abs (velocidad) >= velocidadMax;
}
if (pl1._y - pl1._height / 2 < pared1._height) {
pl1._y = pared1._height + pl1._height / 2;
}
if (pl1._y + pl1._height / 2 > pared2._y) {
pl1._y = pared2._y - pl1._height / 2;
}


Como diría el Sr. Burns, "Excelente", pero nos falta el movimiento de la otra paleta, cosa que veremos más tarde (el motivo también, lo analizaré después, pero básicamente se debe a que es lo más difícil de lograr)

Moviendo la pelota

Uh, ahora se complica, recordemos que la idea era alejarnos de la velocidad en x y en y. Pero no del todo, lo que vamos a hacer es seguir empleando velX y velY como variables, pero de otro modo, más trigonométrico si se quiere.

La velocidad en x va a ser igual a la velocidad a la que queremos se mueva la pelota multiplicada por el seno del ángulo (medido en radianes) de la dirección en la que se mueve.
Lo mismo va para la velocidad en y, pero trabajando con el coseno del ángulo.

Como diría un francés, Oh la la.

Pero no es tan complicado. La idea es ésta (aún no es el código terminado, es un esbozo):

Código :

velX = velPelota * Math.sin (angulo);
velY = velPelota * Math.cos (angulo);


Ahora ya no parece tan difícil, vamos a ubicarlo donde corresponde, dentro del EnterFrame, abajo de todo lo demás.

Código :

velX = velPelota * Math.sin (angulo);
velY = velPelota * Math.cos (angulo);
//
pelota._x += velX;
pelota._y -= velY;


No nos olvidemos de las variables:

Código :

var vel, velY, velY, angulo:Number;
var velPelota:Number = 8;


Ahora, hay que determinar el ángulo.

Vamos a hacerlo simple. En radianes, toda la circunferencia se divide en 2Pi (ahora vendría útil el simbolito de Pi). Es decir que 360º equivalen a 6,2830.... Vamos a ver algunos valores:

360º = 2Pi
180º = Pi
90º = Pi/2

En realidad, usar ángulos en Flash es más intuitivo cuando trabajamos con radianes. Así, vamos a tener que si la pelota se mueve hacia arriba el ángulo va a ser igual a 0 ó 2Pi, cuando va hacia abajo va a ser igual a Pi, cuando se mueve a la derecha a Pi/2 y hacia la izquierda 3*Pi/2.

En primer lugar vamos a programar el rebote contra las paredes, por ser el más simple. Pero no vamos a usar hitTest porque los resultados no siempre son los mejores. Simplemente vamos controlar la posición de la pelota respecto a las paredes.

En segundo lugar, hay que ver cuál es el ángulo en el que la pelota debe ir luego de golpear las paredes. Por suerte, no hay ecuaciones complicadas ni nada por el estilo, es más, la cuenta que vamos a realizar es muy simple. Para rebotes contra objetos horizontales (como las paredes), simplemente, el ángulo resultante es igual a Pi, menos el ángulo de incidencia; para rebotes contra objetos verticales (como las paletas), el ángulo resultante es igual a 2*Pi, menos el ángulo de incidencia.

Código :

if (pelota._y <= pared1._height) {
// Ubicar la pelota lejos de la pared para que no siga activando este if
pelota._y = pared1._height + 1;
// Cambiamos el ángulo
if (angulo <= Math.PI) {
angulo = Math.PI - angulo;
} else {
angulo = 3 * Math.PI - angulo;
}
}
if (pelota._y >= pared2._y) {
//
pelota._y = pared2._y - 1;
//
if (angulo <= Math.PI) {
angulo = Math.PI - angulo;
} else {
angulo = 3 * Math.PI - angulo;
}
}


Si quieren probarlo, definan la variable ángulo como: Math.PI / 36 por ejemplo y vean como la pelota rebota contra las paredes.

Por cierto, el if (angulo <= Math.PI) { evita que el ángulo adquiera valores negativos. No porque no funcione, en realidad, -Pi es igual a Pi. El problema se presenta cuando queremos que la IA reconozca si la pelota se mueve hacia la derecha o la izquierda. Si se mueve a la derecha, el valor del ángulo está entre 0 y Pi, y si lo hace hacia la izquierda entre Pi y 2Pi. Un número negativo nos rompe todos los esquemas.


Ok, pero eso no es todo, ahora se complica un poco. La pelota debe rebotar contra las paletas.

Nuevamente, no vamos a usar hitTest, más que nada debido a que se ve afectado muy fácilmente por la velocidad de la pelota. Entonces, hacemos algo similar a lo que hacíamos con las paredes y verificamos la posición en x y en y de la pelota.

Código :

if (pelota._y > pl1._y - pl1._height / 2 - pelota._height / 2 && pelota._y < pl1._y + pl1._height / 2 + pelota._height / 2 && pelota._x < pl1._x + pl1._width / 2 + pelota._width / 2 && pelota._x > pl1._x) {
pelota._x = pl1._x + pl1._width / 2 + pelota._width / 2 + 5;
angulo = 2 * Math.PI - angulo;
}
if (pelota._y > pl2._y - pl2._height / 2 - pelota._height / 2 && pelota._y < pl2._y + pl2._height / 2 + pelota._height / 2 && pelota._x > pl2._x - pl2._width / 2 - pelota._width / 2 && pelota._x < pl2._x) {
pelota._x = pl2._x - pl2._width / 2 - pelota._width / 2 - 5;
angulo = 2 * Math.PI - angulo;
}


En mi opinión, eso no es complicado, no hay que olvidar que lo parece solo porque hay muchas verificaciones dentro del if (no uso varios ifs para achicar el código), el concepto, en realidad, es muy simple. Igual, por el momento, no introducimos variaciones al ángulo. Eso viene después.

Bien, dirán, ya casi terminamos. Pues si, sólo falta mover a la Computadora y desviar la pelota dependiendo del lugar en el que golpee a la paleta. Empecemos por la paleta controlada por la computadora.

La paleta controlada por la computadora

Ésto es difícil. Por un minuto, analicemos cómo se juega al pong. Uno no piensa en dónde está la pelota sino en dónde va a estar. Eso puede resultar muy simple para un ser humano (bastante intuitivo también), pero no para la computadora.

Pero, por un momento supongamos que programamos a la computadora para ir exactamente al lugar en el que va a estar la pelota. Que ocurriría?

Lógicamente el juego sería imposible, al no equivocarse, la computadora sería un contrincante demasiado perfecto y el juego aburriría, es decir, la pelota rebotaría hasta que el usuario se equivoque o abandone nuestro juego para siempre.

Entonces qué hacemos?

Hay varias soluciones (éstas son las que se me ocurrieron, no quiere decir que sean las únicas o las mejores):

1) Con un random hacemos que la computadora se "equivoque" a veces.

2) La computadora se puede mover más lentamente que el jugador

3) La computadora sólo comienza a calcular la posición de la pelota cuando esta está peligrosamente cerca de la paleta.

4) No tomamos en cuenta dónde va a estar la pelota y nos enfocamos en dónde está.

5) No todo en la vida es blanco o negro, podemos mezclar la idea 1 y la 3, supongo que ya se imaginarán cómo.


A decir verdad, el secreto de un buen juego de computadora es la IA o inteligencia artificial. Sin importar cómo la programemos, de ella suele depender cuánto se va a divertir alguien jugando. Pero, como vemos, evitar que sea perfecta y que se comporte de manera natural al mismo tiempo puede ser un gran reto. Lo más importante es hacer hincapié en pensar cada una de las distintas posibilidades. Veamos los contras de las propuestas anteriores.

1) Ahh, eso es subestimar al usuario. Si el que está jugando nuestro juego nota que una de cada 5 veces, la computadora se equivoca, pero el resto del tiempo es invencible, nuestro juego pierde la gracia. Es una mala idea.

2) Esta idea puede parecer buena, pero entonces no sería un juego parejo, cosa que el usuario también nota. Es más, supongamos que encontramos la velocidad justa para que una de cada 5 veces (por decir un número) la pelota se le escape, si cambiamos la velocidad de la misma, esto ya no ocurrirá y el juego se va al demonio. Claro que se puede armar una relación entre la velocidad de la pelota y la de la paleta controlada por la computadora, pero es complicarse inutilmente, porque el juego no es parejo y al sentir que está en ventaja, el usuario puede llegar a perder lo que lo impulsa a jugar.

3) Esta respuesta se acerca al ideal, si logramos determinar en qué punto debe comenzar a jugar bien y hacemos un movimiento aleatorio el resto del tiempo, puede que el juego quede bien, pero sería un error tomar la primer buena idea que se nos ocurre sin pensar en todas las otras posibilidades.

4) No, la solución más fácil suele ser la peor y este es el caso, recordemos que el producto final debe ser bueno, no fácil de programar. No hay que dejar que las soluciones fáciles nos tienten, ese es el lado Vil Gates de la programación, el lado oscuro, si se quiere, y debe ser evitado.

5) Bien, ésta es la clase de solución ideal, el producto de pensar en todo, fuera de la caja como dicen en USA. Tomamos una buena idea y una mala y logramos lo más similar al comportamiento humano que podemos encontrar. En este caso, ésta va a ser la solución elegida, pero eso no quiere decir que no existan otras posibilidades en las que no pensamos. En última instancia, podemos detenernos a analizar cómo juegan personas reales a nuestro juego y ver cómo podemos imitar ésto.



Bueno, no me voy a detener mucho en este punto. Lo que quería mostrar era cómo había que "atacar" el problema y no voy a explicar el código que posteo a continuación. Lo dejo sólo en caso de que alguien tenga alguna duda sobre cómo hacerlo.

Código :

if (angulo < Math.PI && pelota._x > 275) {
if (!enPosicion && !recienIniciado) {
if (pl2._y < pelota._y) {
if (angulo < Math.PI / 2) {
Ypos = Math.abs (pelota._y - pl2._y) / 2 + pelota._y;
} else {
Ypos = pelota._y - 100;
}
} else {
if (angulo < Math.PI / 2) {
Ypos = pelota._y + 100;
} else {
Ypos = Math.abs (pelota._y - pl2._y) / 2 - pelota._y;
}
}
}
//
// Si se reemplaza en 130 por un número más alto, la computadora actúa de forma más hábil
// Si se lo reemplaza por uno más bajo (digamos, 70 ó 100) se la entorpece.
// Este es el mejor modo de cambiar la dificultad del juego.
// Un número demasiado alto (mayor a 200 ó 250) vuelve a la computadora prácticamente invencible. No es recomendable
if (Math.abs (pl2._y - pelota._y) < 130 || recienIniciado) {
enPosicion = true;
}
veloc = velocidadMax;
if (enPosicion) {
if (angulo < Math.PI / 4 || angulo > 3 * Math.PI / 4) {
if (Math.abs (pelota._x - pl2._x) > 70) {
dist = (Math.abs (pelota._y - pl2._y) >= 100) ? 100 : Math.abs (pelota._y - pl2._y);
} else {
dist = 120;
}
} else {
if (Math.abs (pelota._x - pl2._x) > 70) {
dist = (Math.abs (pelota._y - pl2._y) >= 60) ? 60 : Math.abs (pelota._y - pl2._y);
} else {
dist = 80;
}
}
if (pl2._y > pelota._y) {
Ypos = pelota._y - dist;
} else {
Ypos = pelota._y + dist;
}
}
} else {
accion = undefined;
enPosicion = false;
veloc = velocidadMax / 3;
if (angulo > Math.PI / 2 && angulo < 3 * Math.PI / 2) {
Ypos = pl2._y + 100;
} else {
Ypos = pl2._y - 100;
}
}
vel = (pl2._y - Ypos) / 10;
if (Math.abs (vel) > veloc) {
if (Ypos > pl2._y) {
velocidad = -veloc;
} else {
velocidad = veloc;
}
} else {
velocidad = vel;
}
pl2._y -= velocidad;


También, hay que declarar las variables que pensamos usar:

Código :

var Ypos, veloc, dist, posFinalY:Number;
var enPosicion, recienIniciado:Boolean;


En caso de querer crear múltiples niveles de dificultad, hay que cambiar el valor indicado por los comentarios

Bien, como ya dije, no voy a explicar como funciona la inteligencia artificial, por lo que pasaré al siguiente asunto. Pero no sin antes aclarar que en el código de arriba no hay ningún random en todo el código de la IA. Esto es porque mientras me esforzaba por hacer que la paleta actuara de forma "natural" logré hacer que se equivoque a veces (no muchas) simplemente usando un código imperfecto (lo que, en mi opinión es perfecto). Tampoco necesité considerar dónde debía estar la pelota, esto se debe a que cuándo lo hice, la IA era demasiado buena y al inducirla a fallar con un random quedaba bastante mal.

De todos modos, no debería resultarle difícil a nadie modificar la IA a su gusto, aunque recomendaría a quien lo haga que considere emplear la vaiable Ypos como el destino de la paleta para que ésta se mueva con easing.

Rebote contra las paletas

Hasta ahora, tenemos un juego interesante, que no se aleja demasiado del que teníamos en un principio. Claro, el código es muy distinto, pero hace casi lo mismo. Falta agregar lo principal, quizá el propósito de todo este tutorial, la variación en el ángulo y el llamado "efecto", donde la pelota seguirá una trayectoria curvilínea al ser golpeada por una paleta en movimiento.

Manos a la obra, comencemos por variar el ángulo.

Ahora que tenemos el movimiento de la pelota dictado por un ángulo en radianes y la constante velocidad en lugar de va velocidad en x y en y, podemos hacerlo sin problemas. En este caso, la variación en el ángulo dependerá sólo de la distancia al centro de la paleta.

Código :

if (pelota._y > pl1._y - pl1._height / 2 - pelota._height / 2 && pelota._y < pl1._y + pl1._height / 2 + pelota._height / 2 && pelota._x < pl1._x + pl1._width / 2 + pelota._width / 2 && pelota._x > pl1._x) {
pelota._x = pl1._x + pl1._width / 2 + pelota._width / 2 + 5;
anguloResultante = 2 * Math.PI - angulo;
dist = Math.max (pelota._y, pl1._y) - Math.min (pelota._y, pl1._y);
if (pelota._y > pl1._y) {
anguloMaximo = Math.PI;
} else {
anguloMaximo = 0;
}
//
variacionAngulo = anguloMaximo * (dist / (pl1._height / 2));
//
angulo = (anguloResultante + variacionAngulo) / 2;
//
// Acá abajo, va el código para el golpe con efecto!
}
if (pelota._y > pl2._y - pl2._height / 2 - pelota._height / 2 && pelota._y < pl2._y + pl2._height / 2 + pelota._height / 2 && pelota._x > pl2._x - pl2._width / 2 - pelota._width / 2 && pelota._x < pl2._x) {
pelota._x = pl2._x - pl2._width / 2 - pelota._width / 2 - 5;
anguloResultante = 2 * Math.PI - angulo;
dist = Math.max (pelota._y, pl2._y) - Math.min (pelota._y, pl2._y);
if (pelota._y > pl2._y) {
anguloMaximo = Math.PI;
} else {
anguloMaximo = 2 * Math.PI;
}
//
variacionAngulo = anguloMaximo * (dist / (pl2._height / 2));
//
if (variacionAngulo < Math.PI) {
variacionAngulo += Math.PI;
}
//      
angulo = (anguloResultante + variacionAngulo) / 2;
}


Listo, eso soluciona el tema para ambas paletas, siempre que hayamos definido las variables:

Código :

var anguloResultante, anguloMaximo, dist, variacionAngulo:Number;


Efecto

Ahora el efecto. Bien, en este caso lo hacemos lo más personalizable posible. Para lo que usamos todas estas variables. Al modificar el juego, hay 3 variables que hay que tener en cuenta, el resto, simplemente, no deberían cambiarlas:

duracionSeg: determina cuántos segundos dura el efecto (no hay necesidad de que sea redondo).
variacionFinal: determina cuánto varía el ángulo desde el principio hasta el final del efecto.
duracionFrames: Sólo se debería cambiar el 30 por los fps de la película para que duracionSeg indique realmente los segundos que dura el efecto.

Código :

var variacionFinal:Number = Math.PI / 2;
var duracionSeg:Number = 1.5;
var duracionFrames:Number = Math.round (duracionSeg * 30);
var variacionFrame:Number = variacionFinal / duracionFrames;
//
var duracion:Number;
var dir, dirPaleta:String;
var efecto:Boolean = false;


Este código va junto al rebote de la paleta, necesariamente en la parte de abajo para activar el efecto.

Código :

if (velMax && Math.random () < 1 / 3) {
//
efecto = true;
duracion = 0;
//
// dirPaleta se define junto a las acciones que mueven la paleta controlada el usuario
dir = dirPaleta;
}


Aclaro que dirPaleta la explico más abajo y que: Math.random () < 1 / 3 Significa dentro de un if que el mismo se ejecutará aleatoriamente 1 de cada 3 veces. Si ponemos: Math.random () < 3 / 4 Se ejecutará 3 de cada 4 veces. Supongo que ya le agarraron la mano. Verán que es muy útil cuando tenemos sucesos que pueden o no ocurrir aleatoriamente.

Ahora, el siguiente código va separado, es el que realmente varía el ángulo de la pelota:

Código :

if (efecto && duracion < duracionFrames) {
duracion++;
if (dir == "arriba") {
//
// Con este if evitamos que el ángulo salga de los límites (0 y 2Pi)
if (angulo - variacionFrame > 0) {
//
angulo -= variacionFrame;
} else {
//
angulo -= variacionFrame - Math.PI * 2;
} else {
if (angulo + variacionFrame < Math.PI * 2) {
//
angulo += variacionFrame;
} else {
//
angulo += variacionFrame + Math.PI * 2;
}
}
} else {
efecto = false;
}


Por último, el siguiente código se ubica debajo de las acciones que muevemn a la paleta controlada por el usuario, debajo de la línea "pl1._y -= velocidad;":

Código :

velMax = Math.abs (velocidad) >= velocidadMax;
dirPaleta = (velocidad < 0) ? "arriba" : "abajo";


Conclusión

No se olviden que el código en el .fla está más completo. Es decir, plagado de comentarios que no valía la pena incluir en éste tutorial, para el que sólo dejé unos pocos.



Básicamente, eso es todo, aunque sigue faltando la conclusión. Pero como probé hacer 15 conclusiones distintas y ninguna me convencía, opté por el mensaje directo:


NO COPIEN EL CÓDIGO!!!!!

LEAN EL TUTORIAL Y TRATEN DE HACERLO POR USTEDES MISMOS. SI ELIGEN LA VÍA FÁCIL AHORA, SUS ALMAS ESTARÁN PERDIDAS POR LA ETERNIDAD Y VAGARÁN SIN SENTIDO, INCAPACES DE SUPERARSE Y SALIR DEL COPY PASTE.


Pero claro, si quieren la vía fácil es su problema.

De todos modos, espero que este tutorial les haya servido de algo.

Ah y, por cierto, entre el tutorial y el juego, se me fueron como 18 horas. Especialmente tratando de solucionar los detalles que ocurrían gracias a ángulos rebeldes. Realmente no se pueden imaginar el trabajo que me dieron los malditos (eso de que sólo quería ángulos entre 0 y 2Pi casi me mata). Por lo que les va de advertencia: NO ES FÁCIL! no se apuren, tómenlo con calma y no pregunten sin antes haber agotado hasta su último recurso.




Nota1: Si conocen a fondo AS, probablemente, estén familiarizados con getBounds, que ahorra tiempo al sacar el molesto _height/2. Quizá se pregunten porqué no lo usé, bien, la respuesta es que se me ocurrió demasiado tarde y era más cómodo dejar el código como estaba en vez de cambiarlo.

Nota2: Como no necesita ninguna explicación, este tutorial no incluye cómo hacer el puntaje. En caso de dudas, el .fla lo explica en los comentarios.

Nota3: En el .fla, creo los ángulos con una función para evitar que los ángulos sean demasiado similares a Pi ó a 0. Esto es para evitar que la pelota tarde demasiado en llegar de un lado a otro. Además, la función tiene un extra (que no empleo) que permite elegir si el ángulo hace que la pelota se mueva hacia la derecha o izquierda. El parámetro es dirección y puede ser "der" o "izq".

Por HernanRivas

Claber

3416 de clabLevel

26 tutoriales

 

Argentina

msie
Citar            
MensajeEscrito el 08 Feb 2007 01:50 pm
muchas gracias! andaba buscando algun juego del pong para el XP :) ahora lo comienzo a hacer. :wink:

Por zombieek

139 de clabLevel



 

la patagonia

firefox
Citar            
MensajeEscrito el 13 Ene 2009 05:21 pm
No funciona el link del juego para bajarlo =(

Por Juaniix

85 de clabLevel



 

firefox
Citar            
MensajeEscrito el 10 Jun 2010 05:51 pm
HOola? no funciona el link del juego para bajarlo!!

Por eriko

36 de clabLevel



 

Barcelona

msie

 

Cristalab BabyBlue v4 + V4 © 2011 Cristalab
Powered by ClabEngines v4, HTML5, love and ponies.