Nunca antes había estudiado ningún lenguaje de programación salvo alguna pequeña incursión en visual basic que no tuvo mayor importancia que un par de clases en la facultad. Hace aproximadamente 1 mes que comencé a estudiar el lenguaje de programación Actionscript 3.0 y la necesidad surgió por un trabajo que me intrigo y me dio ganas de comenzar a investigar que era esto de hacer juegos, webs, animaciones o tantas otras cosas con Flash. Así comencé a estudiar día tras día en cada momento libre que encontraba para llegar a desarrollar aunque sea un juego simple en menos de 1 mes.
Y acá estoy, cumpliendo no solo con mi meta sino yendo un poco más allá y traduciendo un tutorial que me ayudo muchísimo a aprender las bases fundamentales de este maravilloso lenguaje.
Primero quiero agradecerle a Michael James Williams quien acepto que realice esta traducción incluyendo algunos agregados personales que fuí aprendiendo.
Si saben ingles y quieren seguir el tutorial desde su Blog pueden acceder a él en la dirección: http://gamedev.michaeljameswilliams.com/ Les recomiendo que lean muy detenidamente su Blog ya que tiene muchos artículos interesantes sobre el tema y suele recomendar webs, artículos e información muy interesante. También pueden seguirlo por su Twitter.
El tutorial abarca desde una introducción general de Actionscript 3.0 hasta lograr un juego completo con niveles, puntuación, sonido, saves y diferentes enemigos.
Después del salto una pequeña muestra de lo que haremos al finalizar la primera parte de este tutorial.
Leer la primera parte del Tutorial...
Aclaración: Este juego es una primera muestra de lo que vamos a terminar haciendo, para el tutorial arme algo más simple para no marearnos con demasiada información junta, por eso decidi no incluir el código y animación del humo de las ruedas de los tanques aún, pero igualmente lo vamos a agregar más adelante).
Algunos Mitos
Cuando empecé a involucrarme en este mundo extraño debo reconocer que pensaba que programar era algo imposible, que necesitaba una dedicación eterna para lograr algo básico y que necesitaría muchos meses de estudio para llegar a lograr a hacer mis propios códigos. La verdad es que me equivocaba. Es verdad que cuando uno no tiene conocimiento ver un código de 100 líneas parece chino básico, pero la verdad es que después de los primeros días de estudio el código se empieza a hacer tan familiar que uno logra leer lo que pasa más que lo que dice, esto no quiere decir que con 1 mes de estudio uno pueda hacer cualquier cosa, pero si logra tener una base lo suficientemente sólida para involucrarse en códigos más elaborados y complejos (aunque hay códigos o paquetes, como los de física, que uno puede usarlos directamente aunque no los entienda, pero ya hablaremos de eso).
Lo que es realmente cierto es que uno necesita mucha dedicación para poder programar, hay que prestar mucha atención a la sintaxis del código, ya que un simple cambio de mayúscula a minúscula, una doble letra, un espacio de más y el código puede comportarse de manera extraña o no responder en absoluto.
De que trata este tutorial?
Mi idea al traducir este tutorial fue la de acercar a mucha gente que no habla ingles uno de los mejores tutoriales que encontré en la web. Es completo, claro, descriptivo y preciso. Espero mantener el espíritu del tutorial original para facilitarles a todos los interesados el mismo nivel de aprendizaje que yo tuve.
Vamos a empezar desde lo básico, partiendo de la idea que los que lean esto nunca vieron nada de programación, lo cual también puede ayudar a repasar algunos conceptos para los que vengan de otros lenguajes o incluso los que ya sepan Actionscript 2.0 y estén aprendiendo a pasar sus códigos a Actionscript 3.0.
El juego que armaremos será un "Avoider Game" o sea que nuestro objetivo será esquivar los enemigos que aparezcan en la pantalla con nuestro personaje. Divertido, no? pues eso va a depender de como lo hagamos.
Este tutorial esta armado para usar con el programa Adobe Flash CS4, el cual pueden conseguir en: Adobe.
También pueden bajar una versión Trial en ese mismo link.
Primeros pasos
Hay varias formas de implementar el código de Actionscript 3.0 en una película de Flash. Se puede hacer de forma directa sobre la línea de tiempo o se puede aplicar mediante archivos de actionscript externos al documento.
La primera opción no es muy recomendable por 2 motivos principalmente, si necesitamos encontrar una parte especifica del código para modificar y no estamos seguros en que fotograma lo pusimos debemos revisar todos los fotogramas que contengan alguna línea de código para encontrar donde esta lo que buscamos. Código es poco práctico al hacer trabajos en colaboración con alguien, por ejemplo, si necesitamos enviarle el arte de nuestra película o juego a un ilustrador para que modifique algo o desarrolle la parte gráfica vamos a tener que enviarle todo el archivo con el código incluido, esperar a que termine de modificarlo y cuando lo recibamos vamos a poder seguir trabajando sobre el archivo. Si en cambio usamos códigos externos de actionscript mientras el ilustrador hace la parte gráfica nosotros podemos seguir escribiendo el código sin que eso genere algún problema al compilar el arte y el código. También programar fuera de flash nos permite tener todo el código más ordenado e incluso reutilizarlo en otro proyecto más adelante.
Yo recomiendo usar el código directamente en la línea de tiempo solamente para algunas películas donde no hay demasiado para programar.
Siguiendo esta idea nuestro proyecto va a partir de 5 premisas principales.
- Usaremos 1 solo layer.
- Usaremos 1 solo frame.
- No pondremos nada en el escenario.
- No usaremos códigos en la línea de tiempo.
- No usaremos códigos dentro de los símbolos.
A pesar de que este tutorial está dirigido a la programación en Actionscript tratare de explicar algunas cuestiones básicas de Flash para los que nunca utilizaron el programa.
Si nunca usaron Flash quizás las premisas de las cuales partimos no les parezcan importantes, pero realmente son necesarias para mantener el proyecto lo más ordenado y limpio posible.
Estas premisas son importantes para marcar el punto de partida de nuestro juego, pero más adelante vamos a tener que realizar algunas modificaciones que van en contra de lo que planteamos (especialmente al añadir un Preloader).
Manos a la obra
Comencemos de una vez abriendo un nuevo archivo de Flash Cs4. vamos a Archivo > Nuevo > Archivo de Flash (AS 3.0).
Antes de empezar hagan click en Archivo > Guardar y armen una carpeta donde van a guardar todos los archivos que van a ir armando. Para mantener todo ordenado les recomiendo que pongan sus iniciales al final de la carpeta. Como referencia yo voy a llamar a mi carpeta "AvoiderGame-INZ". De esta forma si deben pasarle un archivo a alguien para que lo vea esa persona va a saber que ese archivo es de ustedes. Dentro de esa carpeta creen una nueva carpeta que se llame "Classes" donde irán todos los archivos de actionscript que hagamos. Por último guarden su archivo en la carpeta AvoiderGame con el mismo nombre de la carpeta. La mía quedo así "AvoiderGame.fla". Recuerden prestar atención a las mayúsculas y minúsculas.
Cuando tiene todo listo guarden el archivo.
Hay algunos parámetros que debemos chequear antes de empezar a programar el juego. Para ver que todo esté bien vamos a Archivo > Configuración de publicación y ahí en la solapa Flash clickeamos el botón Configuración al lado del menú desplegable Actionscript 3.0.
Dentro de la configuración hacen click en el signo "+" y escribe ./Classes/ para indicar a Flash donde deberá ir a buscar las clases de Actionscript que usemos en nuestro juego. También deben marcar la opción "Declarar instancias de escenario automáticamente" si no está marcada.
Ahora debemos modificar algunas propiedades del escenario del juego. Hagan click en Modificar > Documento y configúrenlo como más les guste. Yo voy a mantener las medidas del tutorial original dándole un tamaño al escenario de 400 x 300 pixels con un fondo gris. No se preocupen si después no les gusta la medida o necesitan cambiarla ya que con solo modificar alguno de estos parámetros el juego seguirá funcionando (aunque quizas deban ajustar algunas de las líneas de código que usaremos en los próximos capítulos). Les recomiendo que mantengan una velocidad de fotogramas de 24 fps (Frames per second).
En la siguiente imagen pueden ver mi configuración:
Bueno, por fín después de tanta vuelta podemos empezar a meternos en el juego, creemos a nuestro enemigo...
Creando a nuestro enemigo
El enemigo será el objeto que nosotros, como jugadores, deberemos esquivar. Antes de darle vida empecemos dibujándolo. Hagan click en Insertar > Nuevo símbolo... y en la caja de texto que aparece escriban como nombre del símbolo Enemy (con E mayúscula) y en "Tipo" elijan Clip de Película (ya que vamos a necesitar animarlo luego). Hagan click en Aceptar. En la Biblioteca van a ver que aparece su nuevo símbolo Enemy.
Si no ven la Biblioteca asegúrense de que Ventana > Biblioteca esta activado. De manera predeterminada Flash nos lleva a editar el símbolo que creamos (Nos damos cuenta por la barra que aparece sobre el escenario).
Si por algún motivo no están dentro del editor del símbolo solo debes hacer doble click en el símbolo de la biblioteca o hacer click derecho y seleccionando editar para lograrlo. Michael eligió una bella carita feliz para hacer al malvado enemigo del juego, quien sabrá porque, pero nosotros vamos a darle una vuelta de rosca y vamos a pensar en un juego de acción, para eso vamos a dibujar un pequeño tanque que haga de enemigo.
Comencé armando el chasis, lo modifique un poco para darle una estética un poco mas blanda y luego le fui agregando detalles. Al momento de dibujarlo pueden hacerlo tan simple o complejo como quieran.
Una vez que tengan su enemigo armado vamos a darle un poco de vida, así que a animar (Si nunca animaron nada en flash les recomiendo que busquen algún tutorial básico de animación en flash). Primero vamos a armar 4 movimientos del tanque:
El primer paso esta en reposo, al segundo solo le estire un poco el cañón para que ayude al movimiento, el tercer paso no solo tiene el cañón más largo sino que también moví las líneas de las ruedas para dar la sensación de que está avanzando. Por último deje las líneas de las ruedas en el mismo lugar que en el paso 3, pero reduje el tamaño del cañón al mismo que tenía en el paso 2. De esta forma conseguimos el siguiente movimiento:
Cuando editamos un símbolo fíjense que hay una pequeña cruz negra, ese es el registro del símbolo. Lo ideal es centrar nuestro enemigo con respecto al centro y para eso tenemos las opciones de alinear (Si no lo ven pueden activarlo yendo a Ventana > Alinear). Ahí deben hacer click en el botón que dice "En escenario" y luego centrar horizontal y verticalmente.
Ahora que ya tenemos a nuestro enemigo armado y listo debemos salir del editor haciendo click en Escena 1. Este es un buen momento para guardar nuestro trabajo, así que hagan click en Archivo > Guardar.
Haciendo que el enemigo haga algo
Hasta ahora no hicimos nada demasiado extraño, solo estuvimos preparando el juego y diseñando a nuestro enemigo, pero eso está por cambiar. Es el momento de empezar a escribir el código que va a indicar el comportamiento de nuestro enemigo. Como dije al principio vamos a trabajar con códigos que van a estar fuera del archivo de Flash, no van a estar ni en la línea de tiempo, ni dentro del símbolo.
Como dije antes los beneficios son varios:
- El código está completamente separado del arte, así que podemos darle a un artista nuestro archivo FLA sin que tengamos que entregarle también el código completo del juego o la película.
- A la vez podemos enviarle a otro programador uno de los archivos de código para que use, sin que tenga que tener todo el resto de los archivos o incluso el arte.
- Si tenemos muchos programadores trabajando juntos es mucho más fácil y práctico a la hora de reunir todos los archivos.
Una vez guardado comiencen tipeando:
package { }
La palabra package simplemente nos indica a nosotros y a Flash que todo lo que va a estar dentro de las llaves es un paquete, o sea, un conjunto de instrucciones. En este caso nosotros vamos a armar un paquete que contenga la clase Enemy y todos sus comportamientos.
package { public class Enemy extends MovieClip { } }
Bueno, veamos estas palabras nuevas una a una.
- class Enemy - Aquí estamos definiendo la clase Enemy, todo lo que aparezca dentro de las llaves {} formara parte de las propiedades de esta clase. Como dijimos antes las clases eran las instrucciones de un objeto, no el objeto en si mismo. Los objetos que luego agreguemos al escenario van a ser instancias de la clase.
- public - Mediante el uso de esta palabra estamos permitiendo que otras películas o archivos de actionscript puedan acceder a la clase Enemy y utilizarla, si en vez de public definimos la clase como private o internal todo el contenido del paquete va a estar restringido a cualquier clase o función externa que intente acceder a ella.
- extends MovieClip - Acá le estamos diciendo a flash que la clase Enemy puede hacer todo lo que hace la clase MovieClip y mucha más, o sea, podemos agregarle funciones, propiedades, variables y todo lo que necesitemos. Por ejemplo, la clase MovieClip nos permite animar a nuestro personaje, pero si queremos darle una barra de vida no vamos a poder, para eso estamos creando esta nueva clase, para programar funciones especificas que nuestro personaje requiere.
Así que agreguemos una nueva línea:
package { import flash.display.MovieClip; public class Enemy extends MovieClip { } }
Ahora que importamos la clase MovieClip flash ya sabe de que se trata y no nos va a dar problemas.
Una vez que la clase esta definida debemos agregar un "Constructor". El constructor es una función que tiene el mismo nombre que la Clase y el archivo donde estamos guardando nuestro paquete y que se activa cada vez que creamos una instancia de la clase. A pesar de que no es obligatorio poner una función constructor es muy recomendable. La diferencia entre una función común (llamada método) y una función constructor es que la función constructor se activa sola cuando se crea un objeto de la clase, mientras que los métodos deben ser llamados desde otro lugar para funcionar. Puede parecer medio extraño, pero ya lo van a entender.
Así que armemos la función constructor teniendo en cuenta lo dicho recién, debe tener el mismo nombre que la clase y el archivo:
package { import flash.display.MovieClip; public class Enemy extends MovieClip { public function Enemy() { } } }
Ahí tenemos nuevamente la palabra public. Fíjense que la función Enemy tiene un par de paréntesis vacios, sobre eso hablaremos más adelante.
Ahora que ya tenemos armada nuestra clase Enemy vamos a tener que linkearla a nuestro objeto en la biblioteca. Salven el archivo AS y vuelvan a abrir el FLA (si no lo cerraron debe estar en una solapa en la parte superior de la pantalla).
Ahora háganle click derecho al símbolo Enemy y pongan Propiedades. Si no ven todas las opciones hagan click en Avanzado y marquen la opción exportar para actionscript. Asegúrense de que en el cuadro de Clase diga Enemy. Si todo está bien al hacer click en el lápiz (marcado en rojo en la imagen) debería abrirse el archivo Enemy.as que estábamos modificando.
Hagan click en aceptar.
Ahora debemos empezar a programar el comportamiento de nuestro enemigo. Por ahora lo que haremos ser:
- Que nuestro enemigo aparezca en la pantalla
- Que nuestro enemigo se mueva desde la parte superior de la pantalla hacia abajo.
package { import flash.display.MovieClip; public class Enemy extends MovieClip { public function Enemy() { x = 100; y = 0; } } }
La clase MovieClip tiene como propiedad los parámetros x e y. Como nuestra clase Enemy heredó las propiedades de la clase MovieClip (recuerden que usamos la palabra extends para heredar las propiedades de esa clase) entonces la clase Enemy también cuenta con esa propiedad.
Las personas familiarizadas con los ejes cartesianos deben estar pensando "No íbamos a poner al enemigo en la parte superior de la pantalla? como puede ser eso posible si la coordenada y es 0". bueno, tienen razón, la coordenada y es 0 por que flash utiliza un eje cartesiano diferente. Ubica el punto x=0 e y=0 en el vértice superior izquierdo de la pantalla. Para que les quede claro miren la siguiente imagen.
Aquí vemos el tamaño de nuestra película, 400 x 300 o sea, 400 px en el eje x y 300 px en el eje y. Tengan en cuenta esto por que cuando tengamos que darle la orden a nuestro enemigo de que se mueva hacia abajo vamos a tener que incrementar el valor de y en vez de disminuirlo.
No se preocupen que con el tiempo se van a acostumbrar.
Ahora vamos a crear una función que se va a encargar del movimiento de nuestro tanque enemigo. Como la clase Movieclip no tiene una función para hacer eso vamos a crearla dentro de nuestra nueva clase Enemy, así que vamos a escribir lo siguiente.
package { import flash.display.MovieClip; public class Enemy extends MovieClip { public function Enemy() { x = 100; y = 0; } public function moveDownABit():void { y = y + 3; } } }
Pasemos un poco de esto en limpio para que entiendan que estamos haciendo.
- function moveDownABit() - Todo lo que se encuentre dentro de las llaves de esta función serán las instrucciones que le daremos al objeto Enemy. Fíjense que dentro de los paréntesis no ponemos nada (ya van a entender porque), también declaramos esta función como publica por si debemos acceder a ella más adelante. Otro punto importante es que esta función esta fuera del Constructor, eso quiere decir que no se ejecutara hasta que la llamemos.
- :void - esta función no devuelve ningún tipo de dato. no se rebusquen con esto aún. Cuando tengamos una función que si devuelva datos voy a explicar esto.
- y = y+3; - esta fórmula aumenta el valor de y en 3 pixels (acuérdense que esto quiere decir que el enemigo va a bajar, no a subir.
Insertando 1 enemigo en el escenario
Guarden el archivo Enemy.as y vuelvan al archivo FLA. Nuestro próximo paso será hacer que nuestro enemigo aparezca en el escenario. Hasta ahora no hicimos más que hacer los planos de nuestro enemigo y darle algunas propiedades de cómo se va a comportar.
Es importante recordar que flash surgió como una herramienta de animación, y a pesar de que se desarrollo mucho desde sus inicios sigue pensando que todo lo que se crea es un MovieClip. Como sabemos la clase MovieClip tiene un Constructor que funciona cuando se crea un objeto MovieClip, por lo tanto vamos a extender esa función para que cree a nuestro enemigo cuando el juego comienza. Pero como accedemos al constructor de nuestro juego? si respondiste "con otro archivo AS" acertaste. Así que abran un nuevo archivo .AS y guárdenlo en la carpeta Classes con el nombre AvoiderGame.as y agreguen las siguientes líneas y vuelvan a salvarlo.
package { import flash.display.MovieClip; public class AvoiderGame extends MovieClip { public function AvoiderGame() { } } }
Esto se está poniendo familiar, no? Más allá de que el nombre de la clase y del constructor cambiaron es exactamente igual que la clase Enemy cuando lo comenzamos a armar. Ahora vamos a linkearlo a nuestro archivo FLA.
Vuelvan al Archivo FLA y asegúrense que no estan dentro del editor de símbolos. Busquen el panel de Propiedades (si no lo ven pueden acceder a el yendo a Ventana > Propiedades y fijarse ue Propiedades este chequeado). Fijense que allí hay una ventana que se llama Clase: ingresen ahí el nombre del nuevo archivo AvoiderGame. Es importante poner bien las mayúsculas y minúsculas. Para chequear que este todo bien si hacen click en el lápiz deberían ir al archivo AvoiderGame.as.
Al linkear el archivo AvoiderGame.as al archivo FLA se transforma en la clase del documento.
Genial, así que ahora podemos editar el constructor de nuestro juego para crear un enemigo al comienzo.
package { import flash.display.MovieClip; public class AvoiderGame extends MovieClip { public function AvoiderGame() { enemy = new Enemy(); } } }
Ahora hay que prestar mucha atención a las mayúsculas. Enemy con E mayúscula hace referencia a nuestra clase, la que creamos recién y que contiene todos los datos y propiedades, mientras que enemy hace referencia a una instancia de esa clase. Para dejarlo un poco más claro piénsenlo como que Enemy es como la palabra auto, mientras que enemy puede ser un fiat 600 o una ferrari o cualquier otro auto, o sea que una clase puede tener miles de instancias, pero una instancia solo tiene 1 sola clase.
En este momento la instancia enemy de la clase Enemy solo está disponible dentro del constructor de la clase AvoiderGame ya que lo declaramos dentro de la función. Como necesitamos que el enemigo esté disponible en todo el juego debemos agregar una línea más. Antes de declarar el constructor declaraos una variable.
package { import flash.display.MovieClip; public class AvoiderGame extends MovieClip { public var enemy:Enemy; public function AvoiderGame() { enemy = new Enemy(); } } }
- public - ya saben a qué se refiere esto.
- var - Define una variable. Una variable es un bloque de información que reservamos para luego darle algún valor.
- enemy:Enemy - "La variable se llama enemy y es una instancia de la clase Enemy".
public function AvoiderGame() { enemy = new Enemy(); addChild( enemy ); }
Acá encontramos una nueva función: addChild(). Esta función nos sirve para insertar un objeto en el escenario. Cualquier objeto que sea añadido a la clase del documento va a aparecer en el escenario (siempre y cuando sus coordenadas x e y aparezcan dentro de la ventana).
Decimos ahora que enemy es un child (hijo) de la clase AvoiderGame, y la clase AvoiderGame es el parent de enemy. Un objeto sólo puede tener un parent pero puede tener tantos childs como sea necesario.
Finalmente podemos probar la película para ver que logramos después de tanto recorrido. Guarden todos los archivos y vayan a Control > Probar película (Ctrl + Enter en PC y Command + enter en Mac ) y deberían tener algo similar a esto:
Genial!!! Ya tenemos el enemigo en el escenario, pero no queremos que aparezca en ese lugar, lo mejor va a ser que aparezca fuera del escenario y que luego baje. Lo que hizo flash fue colocar el punto de registro de nuestro enemigo en el lugar que nosotros le indicamos (x=100 Y=0) Podríamos mover el registro de nuestro símbolo hacia abajo, pero es una solución poco recomendable, lo mejor va a ser cambiar el código para que en vez de aparece en el punto de y=0 aparezca un poco más arriba. En mi caso voy a cambiar el código para que aparezca en el punto y=-40, así que abrimos el archivo Enemy.as y modificamos el constructor así:
public function Enemy() { x = 100; y = -40; }
Ustedes pueden modificar el punto y como mejor se adapte a su símbolo.
Dándole vida al enemigo
A pesar que ya incluimos algunas funcionalidades para que el enemigo se mueva no escribimos ningún código que le diga al enemigo que tiene que moverse, así que pasemos a eso.
La idea es hacer que el juego le diga al enemigo cada cierto tiempo que tiene que moverse, para eso vamos a usar la clase Timer que se adapta exactamente a lo que necesitamos.
Editen el constructor del juego (AvoiderGame.as) creando una instancia gameTimer de la clase Timer. Como la clase Timer es parte de flash no es necesario crear un archivo nuevo de AS.
public function AvoiderGame() { enemy = new Enemy(); addChild( enemy ); gameTimer = new Timer( 25 ); }
Fíjense que el paréntesis después del nombre de la clase Timer no está vacio. Ese numero 25 le está diciendo a flash que queremos un intervalo de 25 milisegundos (1000 milisegundos = 1 segundo), o sea que queremos que algo pase cada 25 milisegundos.
Como queremos que el Timer esté disponible para todo el juego debemos declararlo fuera del constructor como hicimos antes. También la clase Timer es como la clase MovieClip, hay que avisarle a Flash que vamos usarla dentro de nuestra clase y para eso debemos importarla. Así que agreguemos las siguientes líneas:
package { import flash.display.MovieClip; import flash.utils.Timer; public class AvoiderGame extends MovieClip { public var enemy:Enemy; public var gameTimer:Timer; public function AvoiderGame() { enemy = new Enemy(); addChild( enemy ); gameTimer = new Timer( 25 ); } } }
Muy bien, así que ya tenemos nuestro Timer funcionando cada 25 milisegundos, pero a pesar de eso no hay nada conectado al timer, por lo tanto si probamos ahora nuestro juego obtendríamos el mismo resultado de antes. Para solucionar esto sigamos agregando líneas a nuestro código.
public function AvoiderGame() { enemy = new Enemy(); addChild( enemy ); gameTimer = new Timer( 25 ); gameTimer.addEventListener( TimerEvent.TIMER, moveEnemy ); }
- gameTimer.addEventListener - Esto se podría traducir como "Decile al gameTimer que este atento a los parámetros que están entre paréntesis, y cada vez que que escuche que esos parámetros ocurrieron que active la función moveEnemy" Un eventListener es una función que esta todo el tiempo funcionando esperando que algo pase, mientras no pase lo que está buscando no hace nada, pero en el momento en que detecta que los parámetros que pusimos suceden llama al método que pusimos.
- TimerEvent.TIMER - Este evento se activa cada vez que la función Timer completa un intervalo, en nuestro caso este evento se va a disparar cada 25 milisegundos que es el tiempo que nosotros seteamos.
- moveEnemy - Esta es la función que se va a activar cada vez que el evento (TimerEvent.TIMER) ocurra, todavía no escribimos este código, pero lo vamos a hacer en minutos.
package { import flash.display.MovieClip; import flash.utils.Timer; import flash.events.TimerEvent;
Ahora tenemos que agregar la función moveEnemy:
public class AvoiderGame extends MovieClip { public var enemy:Enemy; public var gameTimer:Timer; public function AvoiderGame() { enemy = new Enemy(); addChild( enemy ); gameTimer = new Timer( 25 ); gameTimer.addEventListener( TimerEvent.TIMER, moveEnemy ); } public function moveEnemy( timerEvent:TimerEvent ):void { } }
Nuevamente tenemos información dentro de los paréntesís. Esto le permite al eventListener pasarle información sobre el evento hacia la función moveEnemy en forma de instancia (timerEvent) de la clase TimerEvent. No necesitamos saber sobre esto aún así que prosigamos con nuestro juego.
Todo lo que nos queda por hacer (por ahora) es usar la nueva función para decirle al enemigo que debe hacer. Se acuerdan de la función moveDownABit que pusimos antes? bueno, es tiempo de usarla.
public function moveEnemy( timerEvent:TimerEvent ):void { enemy.moveDownABit(); }
De esta forma le estamos diciendo a la instancia enemy que corra la función moveDownABit. Guarden todo y prueben la película (Control > Probar película (Ctrl + Enter en PC y Command + enter en Mac )).
No pasó nada?... Claro, porque nunca le dijimos al gameTimer que empiece a contar, así que editemos el constructor del juego una vez más.
public function AvoiderGame() { enemy = new Enemy(); addChild( enemy ); gameTimer = new Timer( 25 ); gameTimer.addEventListener( TimerEvent.TIMER, moveEnemy ); gameTimer.start(); }
Vuelvan a guardar todo y prueben la película. Objetivo conseguido!!! El enemigo se mueve hacia abajo de la pantalla a una velocidad constante de 3 pixels cada 25 ms (o 120 px por segundo).Ahora deberemos empezar a crear un poco de interactividad, de otra forma esto no es un juego sino una película.
Creando a nuestro personaje
Primero tenemos que crear el arte de nuestro jugador. En su archivo FLA armen un nuevo símbolo del tipo MovieClip llamado Avatar (revisen la sección sobre cómo crear un enemigo si no recuerdan algo). Dibujen lo que quieran que represente a su personaje. Si en vez de armar un nuevo dibujo quieren usar también un tanque como imagen para el jugador pueden hacer lo siguiente:
• Click derecho en el símbolo del enemigo en la biblioteca y ahí eligen duplicar.
• Va a aparecer una nueva ventana de creación de símbolos.
• Cambian el nombre por el de Avatar y seleccionan MovieClip (recuerden fijarse que el registro quede en el centro).
• Una vez duplicado el símbolo van a poder editarlo, pueden cambiarle los colores, agregarle partes, sacarle cosas o modificar lo que quieran, incluso pueden borrar todo y hacer algo desde cero)
• Recuerden que deben modificar todos los puntos de la línea de tiempo.
•Por último revisen que el punto de registro este bien centrado.
Yo modifique mi tanque para darle un verde claro, no se por que... me gustó y quedó, pero ustedes pueden dedicarle más tiempo y darle más detalles (quizás una pintura camuflada, o algunos brillos).
A esta altura ya sabrán cual es el paso a seguir... pensemos un poco... creamos a nuestro personaje y queremos que sea parte de nuestro juego, debemos dale indicaciones... como hacemos??? Claro!!! un nuevo archivo de Actionscript!!! Así que hagámoslo, abran un nuevo archivo AS y guárdenlo con el nombre de Avatar.as. Tengo que decirles en que carpeta hay que guardarlo? bueno, pero es la última vez vamos a guardarlo en la carpeta Classes que armamos antes. Una vez guardado escribimos dentro del archivo:
package { import flash.display.MovieClip; public class Avatar extends MovieClip { public function Avatar() { } } }
Bien, ya tenemos nuestro código que representa a nuestro avatar, pero como hacía Flash para saber que el símbolo que tiene en su biblioteca tiene algo que ver con el archivo que acabamos de armar? Como vimos antes debemos linkearlos, así que vayan a la biblioteca, click derecho en nuestro Avatar y seleccionen propiedades. Allí seleccionan exportar para Actionscript y le hacen click al lápiz como probamos antes para verificar que el símbolo y el archivo Avatar.as estén linkeados.
Ahora pensemos... que queremos que haga nuestro Avatar? Como primera medida vamos a hacer que siga al puntero del mouse por la pantalla. Podemos modificar la posición de nuestro avatar desde la clase AvoiderGame.as, así que abranla y vamos a editar algunas cosas.
Primero debemos agregar una instancia de nuestro avatar al escenario. Voy a poner todo el código junto, ya que más o menos deben ir entendiendo que es lo que voy escribiendo.
public class AvoiderGame extends MovieClip { public var enemy:Enemy; public var avatar:Avatar; public var gameTimer:Timer; public function AvoiderGame() { enemy = new Enemy(); addChild( enemy ); avatar = new Avatar(); addChild( avatar ); gameTimer = new Timer( 25 ); gameTimer.addEventListener( TimerEvent.TIMER, moveEnemy ); gameTimer.start(); } public function moveEnemy( timerEvent:TimerEvent ):void { enemy.moveDownABit(); } }
Antes de decirles que líneas agregue traten de ver el código. Préstenle atención y traten de pensar que líneas agregue.
Primero y principal declare una variable fuera del constructor para que esté disponible para toda la clase y luego agregue adentro del constructor un código idéntico al que usamos para agregar a nuestro enemigo al escenario, el addChild.
Si prueban el juego ahora van a ver que la instancia de nuestro avatar aparece en la esquina superior izquierda (o sea en el punto x=0 Y=0). Lo que vamos a hacer ahora es decirle a flash que queremos que el avatar comience donde se encuentra el puntero del mouse. Flash contiene 2 propiedades que nos pueden ayudar a lograr eso. mouseX que nos da la coordenada del eje X y mouseY que nos da la coordenada del eje Y. Como dijimos antes, cualquier clase que extienda a la clase MovieClip posee las propiedades de la X y de la Y así que editamos nuestro código de nuevo.
public function AvoiderGame() { enemy = new Enemy(); addChild( enemy ); avatar = new Avatar(); addChild( avatar ); avatar.x = mouseX; avatar.y = mouseY; gameTimer = new Timer( 25 ); gameTimer.addEventListener( TimerEvent.TIMER, moveEnemy ); gameTimer.start(); }
Si prueban el juego el avatar va a aparecer donde está el mouse (es más fácil probarlo usando el atajo del teclado para probar el juego). Ya que usamos el gameTimer para alterar la posición del enemigo cada cierto tiempo también podríamos usar la misma función para hacer que nuestro avatar se mueva.
public function moveEnemy( timerEvent:TimerEvent ):void { enemy.moveDownABit(); avatar.x = mouseX; avatar.y = mouseY; }Pensándolo un poco no es muy bueno el nombre moveEnemy (mover enemigo) para nuestra función ahora, ya que no solo movemos el enemigo, sino también al avatar...
Ya se... cambiémosle el nombre a moveEnemyAndAvatar (mover enemigo y avatar) eso sí que es preciso!!!
public function moveEnemyAndAvatar( timerEvent:TimerEvent ):void { enemy.moveDownABit(); avatar.x = mouseX; avatar.y = mouseY; }
Pero hay un problema... si modificamos el nombre de la función hay que modificar también el eventListener del gameTimer, sino cuando vaya a buscar la función moveEnemy y no la encuentre va a dar error, así que también la cambiamos.
gameTimer.addEventListener( TimerEvent.TIMER, moveEnemyAndAvatar );
Acá solo tuvimos que modificar el nombre de la función, el resto queda igual.
Ahora guarden todo y prueben a su nuevo tanque!!!
Agregando Colisiones
Se deben haber dado cuenta que si nuestro tanque toca al enemigo... no pasa absolutamente nada... Que clase de juego es este??? No es divertido!!! Vamos a corregir eso.
Como le decimos a Flash que un objeto choco contra otro? bueno, es más fácil de lo que se imaginan. La clase MovieClip posee una función llamada hitTestObject que detecta cuando un MovieClip está tocando a otro. Naturalmente nuestro avatar y enemigo tiene la misma función... y saben por qué? piensen un poco... Si pensaron que tienen la misma función porque nuestras clases extienden a la clase MovieClip tienen razón, recuerden que al extender una clase heredan sus propiedades y funciones (Más adelante vamos a ver que hay formas de evitar que una función en particular se herede o incluso modificar una función, pero no nos metamos en eso aún). Como los objetos solo se mueven cuando se activa el gameTimer deberiamos chequear si hay una colisión en la función que es llamada por el eventListener del gameTimer.
public function moveEnemyAndAvatar( timerEvent:TimerEvent ):void { enemy.moveDownABit(); avatar.x = mouseX; avatar.y = mouseY; if ( avatar.hitTestObject( enemy ) ) { } }
- avatar.hitTestObject(enemy) - Esto devuelve un valor true (verdadero) si el child avatar está tocando al child enemy.
- if - if significa si, por lo tanto acá le preguntamos a Flash si (if) los parámetros que ingresamos entre paréntesis son ciertos entonces corre el código que hay entre las llaves {}.
- Tick - Cada vez que se cumple el tiempo del gameTimer vamos a decir que paso un Tick. Todo lo que tenga que pasar va a pasar una vez por Tick.
public function onTick( timerEvent:TimerEvent ):void { enemy.moveDownABit(); avatar.x = mouseX; avatar.y = mouseY; if ( avatar.hitTestObject( enemy ) ) { } }
No se olviden de modificar el nombre de la función en el eventListener del gameTimer, no se preocupen que ya no vamos a tener que modificar esto de nuevo.
Finalmente hagamos que el juego realice una acción cuando el avatar toque al enemigo. Como todavía no vimos ni vidas, ni disparos, ni pantalla de game over, ni nada de lo realmente divertido y encima no tenemos puntuación es una buena oportunidad para mostrarles algo.
Recuerdan el código que utilizamos para decirle al Timer que comience a funcionar? bueno, agreguen esta línea dentro de la función onTick:
public function onTick(event:TimerEvent):void { enemy.moveDownABit(); avatar.x=mouseX; avatar.y=mouseY; if (avatar.hitTestObject(enemy)) { gameTimer.stop(); } }
gameTimer.stop(); sirve para detener el Timer. Ahora guarden y vean que pasa cuando los tanques se tocan.
No se preocupen, el juego no se trabó. Si se fijan la animación de los tanques sigue corriendo, lo que cambio es que ya no está funcionando el Timer, por lo tanto la función onTick no se está corriendo. Eso hace que el enemigo no siga bajando y que el avatar no pueda actualizar las coordenadas de la X e Y del mouse.
Dos cosas que podemos sacar en claro es que:
1. El detector de colisiones de Flash es TERRIBLE!!!
2. Va a ser muy fácil poner una opción de pausa dentro del juego.
Conclusiones
Bueno, ya completaron la primera parte para aprender Actionscript 3.0 desarrollando un juego. Si quieren darle el juego completo a alguien solo deben comprimir toda la carpeta de su juego (el archivo Fla y la carpeta Classes). Pueden bajar el tutorial terminado hecho por mí en el siguiente Link (Carpeta en MediaFire).
Si quieren pasarle a alguien sus archivos, pero no les quieren enviar el código para editar pueden enviarles solo el SWF, para eso vaya a archivo > exportar > exportar película, pónganle un nombre a su archivo y guárdenlo.
Soy consiente de que en este primer tutorial repasamos un poco las cuestiones básicas de Actionscript 3.0, con el correr de los tutoriales vamos a ver más cosas en menos paso, eso en la medida que ustedes vayan practicando e incorporando todo lo que vamos haciendo. En el próximo tutorial vamos a insertar múltiples enemigos y luego vamos a agregar un reloj, una pantalla de game over, vidas y un sistema de puntos.
Espero que les haya gustado y que les sirva esta primera entrega. Cualquier comentario o duda pueden hacerlo en este post. Yo les voy a responder todas las dudas que tengan siempre que sepa cómo resolverlo, y si no lo sé lo podemos averiguar juntos.
2ª parte del tutorial















44 comentarios:
We all [url=http://www.chanelsalesonlineoutlet.com/]Chanel Handbags[/url] know that a authentic Chanel Handbags - http://www.chanelsalesonlineoutlet.com/ designer Chanel Bags handbag is Chanel Bags very expensive. Chanel Bags One is for its materials. They use high quality leather or canvas which should be free of damage. The attached hardware such as the lock has the best quality.The second is for its workmanship. The seams of a real designer bags are actually sewn and not just glued together. High quality zippers, snaps and latches are also used. The lining is well made and sewn into place without wrinkles. The third is for its status. But a bag of more than a grand is still quite a strong pill for most ordinary people. However, there are some very well made replica handbags which cost much lower or even half price of the real one. In fact they are so well made that it may be difficult for the untrained buyer to spot a replica bag.First, quality replica bags are made of the same leather and canvas that are used to make real designer bagsSecond, high quality replica bag has all of the marks of good workmanship that mentioned above.Third, you will also find the designer's logo attached to the replica bags. In many cases, it will take a very well trained individual to tell whether or not the logo on your bag come from the original designer.Forth, a real designer bag has a production number stamped inside to assure that the bag is an original. It will let the buyers to know that the bag was made with caution. But you will also find a unique number inside the well-made replica bags. These bags were also made with care and caution,Messenger Chanel Black embellished flowers and used are an amazing design option for Spring season. Pink and red, dark and Mulberry coloring mixture are exceptional and bag and feminine bow details, unquestionably include festive touch.
genial tu tutorial, muy bueno!!!!
[url=http://www.lv-bags-onsale.com/louis-vuitton-lv-damier-ebene-canvas-evora-mm-41131-white-p-572.html]Louis Vuitton (LV) Damier Ebene Canvas Evora MM 41131 white[/url]
louis vuitton glasses http://www.lv-bags-onsale.com/
[url=http://www.lv-bags-onsale.com/]louis vuitton outlet[/url]
Louis Vuitton mens Business small Briefcase m97038 coffee http://www.lv-bags-onsale.com/louis-vuitton-mens-business-small-briefcase-m97038-coffee-p-516.html Louis Vuitton mens Business small Briefcase m97038 coffee
[url=http://www.truereligion2u.com/]true religion sale[/url] true religion jeans cheap [url=http://www.truereligion2u.com/]true religion sale[/url] cheap true religion jeans for kids [url=http://www.truereligion2u.com/true-religion-jeans-gwen-rambler-medium-p-769.html]True Religion Jeans Gwen - Rambler Medium[/url] cheap true religion jeans http://www.truereligion2u.com/true-religion-jeansricky-big-t-medium-prankster-p-705.html True Religion jeans'Ricky Big T' - Medium Prankster
[url=http://www.truereligion2u.com/]true religion outlet store[/url] true religion jeans for men [url=http://www.truereligion2u.com/]true religion jeans[/url] men true religion jeans [url=http://www.truereligion2u.com/true-religion-jeans-ricky-dark-true-blue-p-655.html]True Religion jeans 'Ricky' - Dark True Blue[/url] true religion jeans on sale http://www.truereligion2u.com/true-religion-jeansjulie-supervixen-p-783.html True Religion Jeans'Julie' - Supervixen
When experiencing on the defensive side, the person ought to learn to not fight back. In recent years the Internet has gained popularity among young people, Air Max has entered a new phase. These shoes are specifically designed to conform to a women's foot.
http://www.tuoitresaigon.com/index.php?do=/profile-287/info/
https://darkwomb.net/blogs/user/TJared
http://www.mojatu.com/Callum31/info/
http://mimidate.com/index.php?do=/profile-1289/info/
http://www.videoonnow.com/Mellisa48
http://yaran.punbb-hosting.com/profile.php?id=896
http://socialous.org/link/318
http://www.rnm44andwoof.com/2012/08/16/vice-president-paul-ryan/
[url=http://louboutinshop.co.uk]christian louboutin[/url] Trails' printable online topo maps offer shaded and un-shaded reliefs, and aerial photos too! Use topographic map functionality to find elevation, print high resolution maps, save a PNG, or just learn the topography around West River. http://dkgoose.com Yqysocxnw [url=http://canadagoosesweden.com]Canada goose outlet [/url]
hkiuyg 056644 [url=http://www.canadagoosestorontofactory.ca]canada goose lodge jacket[/url] 763100 http://www.officialcanadagooseparkas.ca
Thank you for the good writeup. It in fact was a amusement account it.
Look advanced to far added agreeable from you! However, how could we communicate?
Also see my page - Cheap nike nfl jerseys
Aw, this was a really nice post. Taking a few minutes and actual effort to generate a great
article… but what can I say… I procrastinate a lot and don't seem to get anything done.
my web site > www.cheapchirstianlouboutinshoes.com
Asking questions are genuinely good thing if you are not understanding
something fully, except this article gives fastidious understanding even.
Here is my website : nfl jerseys outlet
You made some good points there. I checked on the web to find out
more about the issue and found most people will go along with your
views on this site.
Here is my web page : http://www.customnfljerseysvip.com/
Very good site you have here but I was wanting to know if you
knew of any discussion boards that cover the same topics talked about
in this article? I'd really love to be a part of community where I can get feedback from other knowledgeable individuals that share the same interest. If you have any recommendations, please let me know. Appreciate it!
Also see my site - Christian Louboutin sale
This is a topic which is close to my heart... Thank you!
Exactly where are your contact details though?
Feel free to visit my website - http://www.nfljerseyswholesale49.com/
Wonderful blog! I found it while surfing around on Yahoo News.
Do you have any tips on how to get listed in Yahoo News?
I've been trying for a while but I never seem to get there! Cheers
Also visit my blog ; cheapnikenfljerseysnews.com
[url=http://fans-chicago.com/]Brian Urlacher Jersey[/url]
We wouldn't have automobiles today if people had not had problems getting from place to place quickly? From a place of compassion what do you need to do to stop the waiting game around this one thing?4 While laser toner inhalation has not been proven to cause serious health problems, respiratory tract irritation can occur with exposure to large amounts of laser toner dust
Thought is like hammering a nail Give away what you want more of ? it really worksFree Will, in my view, is the way we perceive our life as well as the way we portray ourselves We using graphology chooses right employees according to business criteria
[url=http://giantsofficialjersey.com/]Women's Victor Cruz Jersey[/url]
[url=http://patriotsofficialshop.com/]Red Stevan Ridley Jersey[/url]
Because, the primary motivating factor in choosing a profession is the 'remuneration' received or monetary benefits involved5Okay, let's begin cleaning that laser printer of the laser toner2
He also wisely addresses the mental processes of Neanderthal and we open another debateAny company that introduces a new product will hire people to try it out before it is introduced to the market Too many people know that insurance is important
Heath Miller Nike Jersey
Von Miller Jersey
"These simple tools will help you feel and look better Unless you wish to take the alien intervention route of 'easy answers' to explain the various things we are discussing, you will have to keep working to understand why Empire and women-hating was so important to those wishing domination and control, as the appropriate means of governance Be with your supporters, at home and away I become sarcastic and abrupt
Charles Tillman Jersey
That money would simply go into Defendant Karam hands, according to the indictment. [url=http://www.vanessabrunosacshop.com]vanessa bruno sacs[/url] "A return to wholeness.". [url=http://www.wintercanadagoose.com]canada goose expedition[/url] Adugexivw
[url=http://www.mulberryinoutlet.co.uk]Mulberry Bayswater[/url] Ogpngkfia [url=http://www.canadagooseparkaca.ca]canada goose[/url] gkninzoxm
All you have to do is provide your ZIP code, e-mail address and device on which you want to receive the messages. [url=http://www.mulberryhandbagssale.co.uk]Mulberry Bags[/url] This profession is in demand especially in the developing countries, and is worldwide appreciated as help and support is provided to the disadvantaged people.. [url=http://www.goosecoatsale.ca]canada goose outerwear[/url] Kwnxzycfz
[url=http://www.pandorajewelryvip.co.uk]pandora Sale[/url] Ntjxfosxr [url=http://www.officialcanadagooseparkae.com]canada goose canada[/url] atdvyeelh
Several clubs have open enrollment. [url=http://www.vanessabrunosacshop.com]vanessa bruno sac [/url] Some will have a steady income potential, while others are seasonal and thus will have spikes when income will be markedly high only to taper off for months at a time. [url=http://www.wintercanadagoose.com]canada goose expedition[/url] Erqvvwwnm
[url=http://www.mulberryinoutlet.co.uk]Mulberry[/url] Jqnhxpqsz [url=http://www.canadagooseparkaca.ca]canada goose[/url] ckntlrlns
adult rpg dating sex games http://loveepicentre.com/faq.php free christian dating and personals online
burberry handbags ssgkyi suix burberry outlet glyvjf dkhe burberry outlet sale jaiuaa qfnj ugg uk gcwqqt tjop ugg boots outlet pzpxpa gyrw ugg boots sale inemvb dsfj ugg outlet qgerck ebzy ugg boots outlet unxgzq yvvl michael kors outlet axecnf vygs michael kors outlet store jbmhqn outz michael kors flats rtpgya yvec longchamp outlet store oqrhva vcbe longchamp sale hfmhtp bimf longchamp handbags busqka hvfm burberry handbags avlxxw qwqs
Gracias! Muy buen tutorial... Sencillo y concreto. Ahora voy a la parte 2! :D
Saludos.
burberry kjhzgo fhwx burberry cespos fmoq www.livebulberryfashion.com sqnfgt irim ugg boots uk swtubl buls ugg sale qkylil kvxs ugg outlet online ineyeo fbpe http://www.8wxc.com cjecei fpxv ugg sale xvzzhq yyfv michael kors handbags outlet tjmuhw zxvu michael kors online outlet wrhqkh iqnz michael kors flats yyyirb lqfm longchamp on sale ywgsmv okhn longchamp sale ibmxkw nwah http://www.dtr6.com gqljkh gbhs burberry outlet online pbyues dwsc
burberry bags vleysv uydc burberry bags nnoxek xgzn burberry drnxaf cqqi www.specjerseys.com jrvcgn fnay ugg boots wholesale auoens jnzf ugg boots sale gmtsdw jzcu ugg boots cheap wpgqux zyyz ugg sale qsbtkt midc michael kors outlet store ilbdpl skck michael kors handbags bonkec gccw michael kors flats kuxqqz qceg longchamp outlet bmxdog kqcb http://www.9dcu.com tzndab qima http://www.dtr6.com ucyofh ipfi burberry outlet xukles otli
burberry bags wmsmgy nvek burberry bags fwvcfm cqzc www.livebulberryfashion.com gkaxmj xgme ugg sale zczrjt wkix ugg boots sale ipuvml uyhz ugg usa pcebmi mrvp ugg boots outlet ayzfno muve ugg sale emijmq qjsz michael kors online outlet cxymoj nknr http://www.02s8.com dnkkcw prwg michael kors outlet zinyzp vxjt longchamp outlet owgemx qjpp longchamp bags on sale hefpxf ybkn longchamp handbags outlet mnoizn mkqe http://www.e4ni.com vxgygs jvfl
www.bulberryfashion2013.com bbfqlv qcpv burberry sale voqdlh trfb burberry bags mtniak khtz uggs outlet kssefw dmrm ugg outlet online aamyle bhmc ugg usa gmocka qqum ugg outlet dvixqj dbdt ugg boots outlet tjppzl wsdz michael kors online outlet jvrvnr jxwb michael kors handbags grgufr agbf michael kors outlet kjpzxb skrw longchamp handbags sale bjvrcd cbbt longchamp outlet ozpvvv tlhy longchamp handbags vyhnhi qsig burberry outlet xybgmd ytdp
burberry bags ndtuqn vbfo burberry ozgtgk zbal burberry sale outlet acesyi zehn ugg boots uk ruzyyn mljb ugg boots mmqwkk tgwc ugg boots sale nmmgfd arvp ugg outlet bitlld nsja http://www.7jcu.com huvrwm lqeh michael kors online outlet ponjir zepa http://www.02s8.com fhmiim krmz michael kors outlet axapnf pwkl longchamp outlet store apaicg cwaa http://www.9dcu.com rixikh jlro longchamp bag ignlqs mciw burberry diaper bag hjyflg wgjh
www.bulberryfashion2013.com wtzdez twbo burberry sale codcuv owxm burberry outlet store gvcabm fxct ugg boots uk obosgy gdhs ugg boots outlet swrzdx bfxg ugg boots sale vflxge ecwo http://www.8wxc.com aluwvm oors ugg boots cheap ajktgz heha michael kors outlet store ezellx nopp michael kors outlet store qvwkqm vojk michael kors diaper bag hpsjot wfkz longchamp outlet online besthl spny longchamp outlet cemrkc plda longchamp bag ojvcay fpua burberry handbags mitpzc vweu
burberry handbags eleuzb qdem burberry bzfzjj qtop burberry outlet online store wpdrmi ivdr uggs uk wihvph gbdz ugg outlet online sale tstiwl gchy http://www.2lv6.com nhxlbh hdcg http://www.8wxc.com qunyhu bvws ugg boots outlet wcijhd ksnw michael kors online outlet cqkoqi icea michael kors 2013 xatdvb cmve http://www.1qpf.com lhznwo mlrd http://www.z8ye.com mubcwo uvxo longchamp bags on sale whiayb kvyz longchamp handbags mblikd oyoq burberry outlet online bluupo txke
[url=http://onlinecasinose25.com ]casino online [/url]INCLUDING BUT NOT LIMITED TO INDIRECT, CONSEQUENTIAL, PUNITIVE http://onlinecasinose25.com online casino 4 card keno casino online so well recognized that during the same hundred years democracy was so
http://groupware.mg.inf.tu-dresden.de/node/45268
disclosure on the network two hulking interest groups behind manipulation and concise with each other to do [url=http://www.ddtshanghaiescort.com]beijing massage[/url] more it
at once it [url=http://www.ddtshanghaiescort.com]beijing massage[/url] is opposite a handful years after this period of lifetime beans futuresnot normal fluctuations substantial
correct for use in construction works In joining to conferences and seminars, construction automaton Association is also [url=http://www.ddtshanghaiescort.com]escort shanghai[/url] committed
My family members all the time say that I am wasting my
time here at net, however I know I am getting familiarity daily by reading thes good articles
or reviews.
my blog :: buy followers
Me and ozzy fucked tougher, trying to show to my own god!
FUCK YES!' prior to cumming inside my warm pussy. were still fucking
Also visit my weblog: hcg injections
generic tramadol tramadol dosage equivalent - buy tramadol uk cheap
[url=http://certifiedpharmacy.co.uk/products/imitrex.htm][img]http://onlinemedistore.com/8.jpg[/img][/url]
pharmacy wall lamp http://certifiedpharmacy.co.uk/products/cephalexin.htm morris pharmacy philadelphia pa [url=http://certifiedpharmacy.co.uk/products/lukol.htm]glen ellyn pharmacy[/url]
prevacid brand pharmacy canadian hydrocodone licensed http://certifiedpharmacy.co.uk/products/terramycin.htm pharmacy regulations in a skilled care facility [url=http://certifiedpharmacy.co.uk/products/lotrisone.htm]lotrisone[/url]
pharmacy choice http://certifiedpharmacy.co.uk/products/allopurinol.htm oxygen regulation california pharmacy board [url=http://certifiedpharmacy.co.uk/products/propecia.htm]managed care pharmacy conferences 2008[/url]
university of new mexico college of pharmacy http://certifiedpharmacy.co.uk/products/lozol.htm massachusetts school of pharmacy [url=http://certifiedpharmacy.co.uk/products/zocor.htm]zocor[/url]
tramadol 100 buy tramadol usa - tramadol online no prescription
I got this web site from my buddy who informed me about this web page and now this time I am browsing this
web page and reading very informative articles
or reviews here.
my weblog; ray ban sunglasses sale
It's actually a great and helpful piece of info. I am satisfied that you shared this useful information with us. Please stay us up to date like this. Thank you for sharing.
Feel free to surf to my site: cheap nike free run
cytotec 200 mg - buy misoprostol online in uk - buy misoprostol online europe
purchase cytotec online - buy cytotec online - buy cytotec online philippines
tramadol online pharmacy order tramadol online cod - buy tramadol online mastercard overnight
Publicar un comentario en la entrada